diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..729f927
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "trailingComma": "all",
+ "tabWidth": 2,
+ "semi": false,
+ "singleQuote": true,
+ "printWidth": 120
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 349d671..02732ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,14 +11,24 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
+ "classnames": "^2.3.1",
+ "gh-pages": "^4.0.0",
"moment": "^2.29.4",
"randomcolor": "^0.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
+ "react-redux": "^8.0.2",
"react-scripts": "5.0.1",
+ "react-tooltip": "^4.2.21",
+ "redux": "^4.2.0",
+ "redux-thunk": "^2.4.1",
+ "thunk": "^0.0.1",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4"
+ },
+ "devDependencies": {
+ "prettier": "2.7.1"
}
},
"node_modules/@ampproject/remapping": {
@@ -3675,6 +3685,15 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -3985,6 +4004,11 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"node_modules/@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@@ -4656,6 +4680,14 @@
"node": ">=8"
}
},
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/array.prototype.flat": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
@@ -5443,6 +5475,11 @@
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA=="
},
+ "node_modules/classnames": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+ "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+ },
"node_modules/clean-css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
@@ -6551,6 +6588,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.185.tgz",
"integrity": "sha512-9kV/isoOGpKkBt04yYNaSWIBn3187Q5VZRtoReq8oz5NY/A4XmU6cAoqgQlDp7kKJCZMRjWZ8nsQyxfpFHvfyw=="
},
+ "node_modules/email-addresses": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
+ "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg=="
+ },
"node_modules/emittery": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
@@ -7779,6 +7821,30 @@
"node": ">=10"
}
},
+ "node_modules/filename-reserved-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
+ "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/filenamify": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
+ "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
+ "dependencies": {
+ "filename-reserved-regex": "^2.0.0",
+ "strip-outer": "^1.0.1",
+ "trim-repeated": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/filesize": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
@@ -8231,6 +8297,95 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gh-pages": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz",
+ "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==",
+ "dependencies": {
+ "async": "^2.6.1",
+ "commander": "^2.18.0",
+ "email-addresses": "^3.0.1",
+ "filenamify": "^4.3.0",
+ "find-cache-dir": "^3.3.1",
+ "fs-extra": "^8.1.0",
+ "globby": "^6.1.0"
+ },
+ "bin": {
+ "gh-pages": "bin/gh-pages.js",
+ "gh-pages-clean": "bin/gh-pages-clean.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/gh-pages/node_modules/array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dependencies": {
+ "array-uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gh-pages/node_modules/async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/gh-pages/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "node_modules/gh-pages/node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/gh-pages/node_modules/globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "dependencies": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gh-pages/node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/gh-pages/node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -8428,6 +8583,19 @@
"he": "bin/he"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -12224,6 +12392,25 @@
"node": ">=0.10.0"
}
},
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/pirates": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
@@ -12232,6 +12419,11 @@
"node": ">= 6"
}
},
+ "node_modules/pjs": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/pjs/-/pjs-5.1.2.tgz",
+ "integrity": "sha512-aoPrjtnYOknat/wiO5hyCkub7LymxSY1BN8gIsW49PfbdPyQiaU+UOo9DfSEhhl48cWLd28IwpERutBveLdQLA=="
+ },
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -13536,6 +13728,21 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -13947,6 +14154,49 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
+ "node_modules/react-redux": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
+ "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.1",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/use-sync-external-store": "^0.0.3",
+ "hoist-non-react-statics": "^3.3.2",
+ "react-is": "^18.0.0",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8 || ^17.0 || ^18.0",
+ "@types/react-dom": "^16.8 || ^17.0 || ^18.0",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0",
+ "react-native": ">=0.59",
+ "redux": "^4"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-redux/node_modules/react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ },
"node_modules/react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -14027,6 +14277,30 @@
}
}
},
+ "node_modules/react-tooltip": {
+ "version": "4.2.21",
+ "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz",
+ "integrity": "sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig==",
+ "dependencies": {
+ "prop-types": "^15.7.2",
+ "uuid": "^7.0.3"
+ },
+ "engines": {
+ "npm": ">=6.13"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/react-tooltip/node_modules/uuid": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
+ "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -14093,6 +14367,22 @@
"node": ">=8"
}
},
+ "node_modules/redux": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
+ "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/redux-thunk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
+ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "peerDependencies": {
+ "redux": "^4"
+ }
+ },
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -15101,6 +15391,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/style-loader": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
@@ -15455,6 +15756,17 @@
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
},
+ "node_modules/thunk": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/thunk/-/thunk-0.0.1.tgz",
+ "integrity": "sha512-pmOMtF63oJP7v+Hka9yRmcoPVeLGZtq2S0NwAh+rE8guWEOcg27dddYsJYZSJiKg4DTs/i71eaUDEvyCuTDNrg==",
+ "dependencies": {
+ "pjs": "*"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/thunky": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
@@ -15524,6 +15836,17 @@
"node": ">=8"
}
},
+ "node_modules/trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
@@ -15770,6 +16093,14 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -19240,6 +19571,15 @@
"@types/node": "*"
}
},
+ "@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "requires": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -19512,6 +19852,11 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
+ "@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@@ -19998,6 +20343,11 @@
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
},
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q=="
+ },
"array.prototype.flat": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
@@ -20574,6 +20924,11 @@
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA=="
},
+ "classnames": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+ "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+ },
"clean-css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
@@ -21378,6 +21733,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.185.tgz",
"integrity": "sha512-9kV/isoOGpKkBt04yYNaSWIBn3187Q5VZRtoReq8oz5NY/A4XmU6cAoqgQlDp7kKJCZMRjWZ8nsQyxfpFHvfyw=="
},
+ "email-addresses": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
+ "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg=="
+ },
"emittery": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
@@ -22284,6 +22644,21 @@
}
}
},
+ "filename-reserved-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
+ "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ=="
+ },
+ "filenamify": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
+ "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
+ "requires": {
+ "filename-reserved-regex": "^2.0.0",
+ "strip-outer": "^1.0.1",
+ "trim-repeated": "^1.0.0"
+ }
+ },
"filesize": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
@@ -22588,6 +22963,78 @@
"get-intrinsic": "^1.1.1"
}
},
+ "gh-pages": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz",
+ "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==",
+ "requires": {
+ "async": "^2.6.1",
+ "commander": "^2.18.0",
+ "email-addresses": "^3.0.1",
+ "filenamify": "^4.3.0",
+ "find-cache-dir": "^3.3.1",
+ "fs-extra": "^8.1.0",
+ "globby": "^6.1.0"
+ },
+ "dependencies": {
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+ }
+ }
+ },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -22727,6 +23174,21 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ }
+ }
+ },
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -25470,11 +25932,29 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
},
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
"pirates": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ=="
},
+ "pjs": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/pjs/-/pjs-5.1.2.tgz",
+ "integrity": "sha512-aoPrjtnYOknat/wiO5hyCkub7LymxSY1BN8gIsW49PfbdPyQiaU+UOo9DfSEhhl48cWLd28IwpERutBveLdQLA=="
+ },
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -26229,6 +26709,12 @@
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
},
+ "prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+ "dev": true
+ },
"pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -26535,6 +27021,26 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
+ "react-redux": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
+ "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==",
+ "requires": {
+ "@babel/runtime": "^7.12.1",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/use-sync-external-store": "^0.0.3",
+ "hoist-non-react-statics": "^3.3.2",
+ "react-is": "^18.0.0",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ }
+ }
+ },
"react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -26595,6 +27101,22 @@
"workbox-webpack-plugin": "^6.4.1"
}
},
+ "react-tooltip": {
+ "version": "4.2.21",
+ "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz",
+ "integrity": "sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig==",
+ "requires": {
+ "prop-types": "^15.7.2",
+ "uuid": "^7.0.3"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
+ "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
+ }
+ }
+ },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -26648,6 +27170,20 @@
"strip-indent": "^3.0.0"
}
},
+ "redux": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
+ "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
+ "requires": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "redux-thunk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
+ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "requires": {}
+ },
"regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -27395,6 +27931,14 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
},
+ "strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
"style-loader": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
@@ -27651,6 +28195,14 @@
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
},
+ "thunk": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/thunk/-/thunk-0.0.1.tgz",
+ "integrity": "sha512-pmOMtF63oJP7v+Hka9yRmcoPVeLGZtq2S0NwAh+rE8guWEOcg27dddYsJYZSJiKg4DTs/i71eaUDEvyCuTDNrg==",
+ "requires": {
+ "pjs": "*"
+ }
+ },
"thunky": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
@@ -27704,6 +28256,14 @@
"punycode": "^2.1.1"
}
},
+ "trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
"tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
@@ -27876,6 +28436,12 @@
"punycode": "^2.1.0"
}
},
+ "use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "requires": {}
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/package.json b/package.json
index 140b8d8..93cf1a7 100644
--- a/package.json
+++ b/package.json
@@ -2,16 +2,24 @@
"name": "codemu",
"version": "0.1.0",
"private": true,
+ "homepage": "https://andrsvsrg.github.io/reactToDo/",
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
+ "classnames": "^2.3.1",
+ "gh-pages": "^4.0.0",
"moment": "^2.29.4",
"randomcolor": "^0.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
+ "react-redux": "^8.0.2",
"react-scripts": "5.0.1",
+ "react-tooltip": "^4.2.21",
+ "redux": "^4.2.0",
+ "redux-thunk": "^2.4.1",
+ "thunk": "^0.0.1",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4"
},
@@ -19,7 +27,9 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
- "eject": "react-scripts eject"
+ "eject": "react-scripts eject",
+ "predeploy": "react-scripts build",
+ "deploy": "gh-pages -d build"
},
"eslintConfig": {
"extends": [
@@ -38,5 +48,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "prettier": "2.7.1"
}
}
diff --git a/public/index.html b/public/index.html
index a5b2e32..eba6c85 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,11 +7,11 @@
content="Web site created using create-react-app"
/>
React App
-
+
diff --git a/src/App.css b/src/App.css
index 104fbe0..23be38d 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,22 +1,28 @@
-*, *::before, *::after {
+*,
+*::before,
+*::after {
box-sizing: border-box;
}
+
.app {
display: flex;
- flex-direction:column;
- font-family: 'Montserrat', sans-serif;
+ flex-direction: column;
+ font-family: "Montserrat", sans-serif;
width: 100%;
height: 100vh;
margin: 0;
- background-color: #20252B;
+ background-color: #20252b;
color: white;
}
+
.header .addtodo_div {
flex-grow: 0;
}
-.draggable_fild {
+
+.draggable-fild {
flex-grow: 10;
}
+
.calendar {
flex-grow: 0;
-}
\ No newline at end of file
+}
diff --git a/src/App.js b/src/App.js
index b7b22f6..fd9e1e7 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,44 +1,30 @@
-import moment from 'moment'
-import React, { useEffect, useState } from 'react'
-
-import Calendar from './components/Calendar/Calendar'
-import AddToDo from './components/AddToDo/AddToDo'
-import Header from './components/Header/Header'
-import ListItem from './components/ListItem/ListItem'
-
-import './App.css'
-
-function App() {
-
- const [todo,setToDo] = useState(
- JSON.parse(localStorage.getItem('items')) || {}
-
- )
-
- useEffect(() => {
- localStorage.setItem('items',JSON.stringify(todo))
- }, [todo])
-
- const [selectedDay, setSelectedDay] = useState(moment())
-
-
-
-
-
- return (
-
- );
-}
-
-export default App;
+import './App.css'
+
+import React, { useEffect } from 'react'
+
+import Calendar from './components/Calendar/Calendar'
+import AddToDo from './components/AddToDo/AddToDo'
+import Header from './components/Header/Header'
+import ToDoList from './components/ToDoList/ToDoList'
+
+import { useSelector } from 'react-redux'
+import { getTodo } from './redux/selectors/todo-selectors'
+
+function App() {
+ const todo = useSelector(getTodo)
+
+ useEffect(() => {
+ localStorage.setItem('items', JSON.stringify(todo))
+ }, [todo])
+
+ return (
+
+ )
+}
+
+export default React.memo(App)
diff --git a/src/components/AddToDo/AddToDo.js b/src/components/AddToDo/AddToDo.js
index c95d5bc..341cff3 100644
--- a/src/components/AddToDo/AddToDo.js
+++ b/src/components/AddToDo/AddToDo.js
@@ -1,77 +1,82 @@
-import { randomColor } from 'randomcolor'
-import React, { useState } from 'react';
-
-import { v4 as uuid } from 'uuid'
-
-import './addToDo.css'
-
-const AddToDo = ({ todo, setToDo, selectedDay }) => {
-
- const [value, setValue] = useState('')
-
-
- function saveToDo() {
- const selectDayStr = selectedDay.format('DDMMYYYY')
- if (value.trim()) {
- if(todo[selectDayStr]) {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectDayStr].push({
- id: uuid(),
- title: value,
- status: true,
- defaultPos: defaultPos(),
- color: randomColor({ luminosity: 'light' })
- });
- setToDo(todoCopy);
- } else {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectDayStr] = [{
- id: uuid(),
- title: value,
- status: true,
- defaultPos: defaultPos(),
- color: randomColor({ luminosity: 'light' })
- }]
- setToDo(todoCopy);
- }
-
-
- setValue('')
- }
- }
-
- function keyPressAddItem(e) {
- const code = e.keyCode || e.which
- if (code === 13) {
- saveToDo()
- }
-
- }
-
- function defaultPos() {
- const width = document.documentElement.clientWidth;
- const height = document.documentElement.clientHeight;
- const defaultWidth = Math.floor(Math.random() * (width < 300 ? width : width - 300)) + 1
- const defaultHeight = Math.floor(Math.random() * (height < 565 ? height : height - 565)) + 1;
- return { x: defaultWidth, y: defaultHeight };
- }
-
-
- return (
-
-
- setValue(e.target.value) }
- placeholder="What to do?"
- type="text"
- onKeyPress={ (e) => keyPressAddItem(e) }
- />
-
-
-
{selectedDay.format('DD.MM.YYYY')}
-
- );
-};
-
-export default AddToDo;
\ No newline at end of file
+import './addToDo.css'
+
+import { randomColor } from 'randomcolor'
+import React, { useMemo, useState } from 'react'
+
+import { v4 as uuid } from 'uuid'
+import { Button } from '../UI/my-button/index'
+import { Input } from '../UI/my-input/index'
+
+import { fixSize, keyCode } from '../../constants/constants'
+import { useDispatch, useSelector } from 'react-redux'
+import { addTask } from '../../redux/actions/todos-actions'
+import { getSelectedDay } from '../../redux/selectors/todo-selectors'
+
+const AddToDo = () => {
+ const [inputValue, setInputValue] = useState('')
+
+ const dispatch = useDispatch()
+ const selectedDay = useSelector(getSelectedDay)
+
+ function onAddTask(e) {
+ const code = e.charCode
+ if (code === keyCode.ENTER) {
+ addNewTask()
+ }
+ }
+
+ function addNewTask() {
+ const newTask = createNewTask(inputValue)
+ dispatch(addTask(newTask))
+ setInputValue('')
+ }
+
+ const selectedDateTitle = useMemo(
+ () => `${selectedDay.slice(0, 2)}.${selectedDay.slice(2, 4)}.${selectedDay.slice(4)}`,
+ [selectedDay],
+ )
+
+ const isDisabledButton = !inputValue.trim()
+
+ return (
+
+
+ setInputValue(e.target.value)}
+ placeholder="What to do?"
+ type="text"
+ onKeyPress={onAddTask}
+ />
+
+
+
{selectedDateTitle}
+
+ )
+}
+
+export default React.memo(AddToDo)
+
+function getRandomDefaultTaskPosition() {
+ const width = document.documentElement.clientWidth
+ const height = document.documentElement.clientHeight
+ const defaultWidth = Math.floor(Math.random() * (width < fixSize.WIDTH_HEADER ? width : width - fixSize.WIDTH_HEADER))
+ const defaultHeight = Math.floor(
+ Math.random() * (height < fixSize.WIDTH_CALENDAR ? height : height - fixSize.WIDTH_CALENDAR),
+ )
+
+ return { x: defaultWidth, y: defaultHeight }
+}
+
+function createNewTask(titleOfNewTask) {
+ return {
+ id: uuid(),
+ title: titleOfNewTask.trim(),
+ isCompleted: false,
+ defaultPos: getRandomDefaultTaskPosition(),
+ color: randomColor({ luminosity: 'light' }),
+ }
+}
diff --git a/src/components/AddToDo/addToDo.css b/src/components/AddToDo/addToDo.css
index a8a0859..370591b 100644
--- a/src/components/AddToDo/addToDo.css
+++ b/src/components/AddToDo/addToDo.css
@@ -1,29 +1,31 @@
-.addtodo_div {
-display: flex;
+.add-todo-block {
+ display: flex;
justify-content: center;
}
-.addtodo_input {
- background-color: inherit;
- border:none;
- border-bottom: 1px solid white;
- color:white;
- outline:none;
- font-size:16px;
+.add-task-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
}
-.addtodo_input:active {
- border:none;
+.add-todo-input {
+ font-size: 16px;
}
-.addtodo_button {
+.add-todo-input:active {
+ border: none;
+}
+
+.add-todo-button {
border: 1px solid white;
- background-color: inherit;
- color:white;
- padding: 7px 12px;
margin-left: 10px;
}
-.addtodo_selected-date {
+
+.add-todo-selected-date {
+ margin-top: 10px;
+ margin-bottom: 10px;
text-align: center;
font-size: 20px;
-}
\ No newline at end of file
+}
+
diff --git a/src/components/Calendar/Calendar.js b/src/components/Calendar/Calendar.js
index 06ad87f..6941a2f 100644
--- a/src/components/Calendar/Calendar.js
+++ b/src/components/Calendar/Calendar.js
@@ -1,159 +1,33 @@
-import moment from 'moment'
-import React, { useState , useEffect } from 'react';
-
-import './calendar.css'
-
-const Calendar = ({ todo, setToDo, setSelectedDay, selectedDay }) => {
-
- moment.updateLocale('en', { week: { dow: 1 } });
-
-
- const [currentWindowCalendar, setCurrentWindowCalendar] = useState(setValuesCurrWindow(moment().year(),moment().month()))
- const [selectMonthValue, setSelectMonthValue] = useState(moment().month());
- const [selectYearValue, setSelectYearValue] = useState(moment().year());
-
- const today = moment();
-
- function setValuesCurrWindow(year, month, day = 1) {
- const selectedDay = moment().set({ 'year': year, 'month': month, 'date': day })
- const startDay = selectedDay.clone().startOf('month').startOf('week')
- let currDay = startDay.subtract(1, 'day').clone();
- const resultArrAllDays = [...Array(42)].map(() => currDay.add(1, 'day').clone());
-
- return resultArrAllDays;
- }
-
-
-
-
- const defaultValues = {
- years : [2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025],
- monthsNames : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- weekDayNames: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- }
-
-
-
- function changeMonthSelect(e) {
- setSelectMonthValue(Number(e.target.value))
- }
-
- function changeYearSelect(e) {
- setSelectYearValue(Number(e.target.value))
- }
-
- useEffect(() => {
- setCurrentWindowCalendar(setValuesCurrWindow(selectYearValue, selectMonthValue))
- }, [selectMonthValue, selectYearValue])
-
- function todayClickButton() {
- setSelectMonthValue(moment().month())
- setSelectYearValue(moment().year())
- setCurrentWindowCalendar(setValuesCurrWindow(moment().year(),moment().month()))
- }
-
- function nextMonthBtn() {
- if(selectMonthValue === 11) {
- setSelectMonthValue(0)
- setSelectYearValue(selectYearValue+1)
- } else {
- setSelectMonthValue(selectMonthValue+1)
- }
- }
- function prevMonthBtn() {
- if(selectMonthValue === 0) {
- setSelectMonthValue(11)
- setSelectYearValue(selectYearValue-1)
- } else {
- setSelectMonthValue(selectMonthValue - 1)
- }
-
- }
-
-
- return (
-
-
- {/* BUTTON PREV MONTH */ }
-
-
-
-
-
-
- {/* BUTTON NEXT MONTH */ }
-
-
-
-
-
{/* DAYS OF WEEK (MN,FR,SUN) */ }
- {
- defaultValues.weekDayNames.map((dayOfWeek, index) => {
- return
{ dayOfWeek }
- })
- }
-
-
{/* DAYS OF MONTH, 42 DAYS */ }
- {
- currentWindowCalendar.map((dateItem) => {
- function addClassDay(item) {
- let classes;
- if (item.day() === 6 || item.day() === 0) {
- classes = 'weekend'
- } else {
- classes = ''
- }
- if(dateItem.isSame(selectedDay, 'day')) {
- classes = 'selectedDay'
- }
-
- return classes;
- }
- function addClassDate(dateItem) {
- let classesDate = dateItem.isSame(today, 'day') ? 'today' : '';
- if(!(dateItem.month() === selectMonthValue)) {
- classesDate += ' notThisMonthColor'
- }
-
- return classesDate;
- }
-
- const dayItemClasses = 'dataItem ' + addClassDay(dateItem)
- const dataClasses = addClassDate(dateItem)
-
- return
{setSelectedDay(dateItem)}} className={ dayItemClasses } key={ dateItem.format('DDMMYYYY') }>
-
- { dateItem.format('D') }
-
-
- {/*{console.log(typeof todo[dateItem.format('DDMMYYYY')])}*/}
- {/*{console.log(typeof todo[dateItem.format('DDMMYYYY')] === 'object' ? todo[dateItem.format('DDMMYYYY')].length ? 'haveTaskForThisDay' : '' : '' )}*/}
-
-
-
- })
- }
-
-
-
-
-
- );
-};
-
-export default Calendar;
\ No newline at end of file
+import './calendar.css'
+
+import React, { useEffect } from 'react'
+
+import CalendarNavigation from './CalendarNavigation/CalendarNavigation'
+import CalendarTable from './CalendarTable/CalendarTable'
+import { createAllDaysForCurrWindow, startWeekFromMonday } from '../utils/data'
+import { useDispatch, useSelector } from 'react-redux'
+import { changeWindowCalendar } from '../../redux/actions/calendar-actions'
+import { mapStateCalendarToProps } from '../../redux/selectors/calendar-selectors'
+
+function Calendar() {
+ const dispatch = useDispatch()
+ const { selectMonthValue, selectYearValue } = useSelector(mapStateCalendarToProps)
+
+ useEffect(() => {
+ startWeekFromMonday()
+ dispatch(changeWindowCalendar(createAllDaysForCurrWindow(selectYearValue, selectMonthValue)))
+ }, [])
+
+ useEffect(() => {
+ dispatch(changeWindowCalendar(createAllDaysForCurrWindow(selectYearValue, selectMonthValue)))
+ }, [selectMonthValue, selectYearValue])
+
+ return (
+
+
+
+
+ )
+}
+
+export default React.memo(Calendar)
diff --git a/src/components/Calendar/CalendarNavigation/CalendarNavigation.js b/src/components/Calendar/CalendarNavigation/CalendarNavigation.js
new file mode 100644
index 0000000..804acd5
--- /dev/null
+++ b/src/components/Calendar/CalendarNavigation/CalendarNavigation.js
@@ -0,0 +1,88 @@
+import './calendarNavigation.css'
+
+import React from 'react'
+
+import { Button } from '../../UI/my-button/index'
+import { Select } from '../../UI/my-select/index'
+import {
+ getCurrentMonth,
+ getCurrentYear,
+ getTodayDayId,
+ monthNamesArr,
+ yearsValues,
+ createAllDaysForCurrWindow,
+} from '../../utils/data'
+import { useDispatch, useSelector } from 'react-redux'
+import { changeSelectedDay } from '../../../redux/actions/todos-actions'
+import { changeSelectMonth, changeSelectYear, changeWindowCalendar } from '../../../redux/actions/calendar-actions'
+import { mapStateCalendarToProps } from '../../../redux/selectors/calendar-selectors'
+
+function CalendarNavigation() {
+ const dispatch = useDispatch()
+ const { selectMonthValue, selectYearValue } = useSelector(mapStateCalendarToProps)
+
+ function onMonthSelectChange(e) {
+ dispatch(changeSelectMonth(Number(e.target.value)))
+ }
+
+ function onYearSelectChange(e) {
+ dispatch(changeSelectYear(Number(e.target.value)))
+ }
+
+ function onTodayClick() {
+ dispatch(changeSelectYear(getCurrentYear()))
+ dispatch(changeSelectMonth(getCurrentMonth()))
+ dispatch(changeSelectedDay(getTodayDayId()))
+ dispatch(changeWindowCalendar(createAllDaysForCurrWindow(getCurrentYear(), getCurrentMonth())))
+ }
+
+ function onNextMonthClick() {
+ if (selectMonthValue === 11) {
+ dispatch(changeSelectMonth(0))
+ dispatch(changeSelectYear(selectYearValue + 1))
+ } else {
+ dispatch(changeSelectMonth(selectMonthValue + 1))
+ }
+ }
+
+ function onPreviousMonthClick() {
+ if (selectMonthValue === 0) {
+ dispatch(changeSelectMonth(11))
+ dispatch(changeSelectYear(selectYearValue - 1))
+ } else {
+ dispatch(changeSelectMonth(selectMonthValue - 1))
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(CalendarNavigation)
diff --git a/src/components/Calendar/CalendarNavigation/calendarNavigation.css b/src/components/Calendar/CalendarNavigation/calendarNavigation.css
new file mode 100644
index 0000000..b91482e
--- /dev/null
+++ b/src/components/Calendar/CalendarNavigation/calendarNavigation.css
@@ -0,0 +1,26 @@
+.calendar-navigation {
+ display: flex;
+ justify-content: center;
+ padding: 15px;
+}
+
+.calendar-button {
+ font-size: 20px;
+ border: none;
+}
+
+.calendar-select {
+ font-size: 20px;
+ width: 140px;
+ padding-left: 12px;
+}
+
+.today-button {
+ border-bottom: 1px solid gray;
+ border-radius: 10px;
+}
+
+.calendar-select:focus,
+.calendar-button:focus {
+ outline: none;
+}
\ No newline at end of file
diff --git a/src/components/Calendar/CalendarTable/CalendarTable.js b/src/components/Calendar/CalendarTable/CalendarTable.js
new file mode 100644
index 0000000..f5cec66
--- /dev/null
+++ b/src/components/Calendar/CalendarTable/CalendarTable.js
@@ -0,0 +1,72 @@
+import './calendarTable.css'
+
+import React from 'react'
+import { isSelectedDay } from '../../utils/data'
+import { useDispatch, useSelector } from 'react-redux'
+import { changeSelectedDay } from '../../../redux/actions/todos-actions'
+import { mapStateToDoToProps } from '../../../redux/selectors/todo-selectors'
+import { getWindowCalendar } from '../../../redux/selectors/calendar-selectors'
+
+function CalendarTable() {
+ const dispatch = useDispatch()
+ const { todo, selectedDay } = useSelector(mapStateToDoToProps)
+ const currentWindowCalendar = useSelector(getWindowCalendar)
+
+ function onChangeSelectedDay(currDay) {
+ dispatch(changeSelectedDay(currDay.id))
+ }
+
+ return (
+ <>
+
+
+ {weekDayNames.map((dayOfWeek, index) => {
+ return (
+
+ {dayOfWeek}
+
+ )
+ })}
+
+
+ {currentWindowCalendar.map((currDay) => {
+ return (
+
{
+ onChangeSelectedDay(currDay)
+ }}
+ className={'dataItem ' + isWeekendClass(currDay) + IsSelectedDayClass(currDay, selectedDay)}
+ key={currDay.id}
+ >
+
{currDay.date.day}
+
+
+ )
+ })}
+
+
+ >
+ )
+}
+
+export default React.memo(CalendarTable)
+
+const weekDayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+function hasTask(currDay, todo) {
+ return todo[currDay.id]?.length ? 'haveTaskForThisDay' : ''
+}
+
+function isWeekendClass(currDay) {
+ return currDay.isWeekend ? 'weekend ' : ''
+}
+
+function isTodayIsThisMonthClasses(currDay) {
+ let classes = currDay.isToday ? 'today ' : ''
+ classes += currDay.isCurrentMonth ? '' : 'notThisMonthColor'
+ return classes
+}
+
+function IsSelectedDayClass(currDay, selectedDay) {
+ return isSelectedDay(currDay, selectedDay) ? 'selectedDay' : ''
+}
diff --git a/src/components/Calendar/CalendarTable/calendarTable.css b/src/components/Calendar/CalendarTable/calendarTable.css
new file mode 100644
index 0000000..eeb6f2e
--- /dev/null
+++ b/src/components/Calendar/CalendarTable/calendarTable.css
@@ -0,0 +1,63 @@
+.calendar-table-dates {
+ width: 100%;
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ grid-template-rows: repeat(6, 1fr);
+ background-color: #404040;
+ grid-gap: 1px;
+ border: 1px solid #404040;
+ margin: auto;
+}
+
+.dataItem {
+ justify-items: end;
+ display: grid;
+ padding: 3px;
+ color: white;
+ background-color: #282b30;
+ min-width: 40px;
+ min-height: 50px;
+ font-size: 10px;
+ cursor: pointer;
+}
+
+.table-daysOfWeek {
+ justify-items: end;
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ margin: auto;
+ width: 100%;
+}
+
+.dayOfWeek-item {
+ min-width: 40px;
+}
+
+.today {
+ display: grid;
+ width: 15px;
+ height: 15px;
+ background-color: #ef4631;
+ border-radius: 50%;
+ justify-content: center;
+ align-items: center;
+}
+
+.selectedDay {
+ border: 1px solid cornflowerblue;
+}
+
+.weekend {
+ background-color: #2e2f36;
+}
+
+.notThisMonthColor {
+ color: #53565d;
+}
+
+.haveTaskForThisDay {
+ background-color: blueviolet;
+ border-radius: 50%;
+ width: 15px;
+ height: 15px;
+}
\ No newline at end of file
diff --git a/src/components/Calendar/calendar.css b/src/components/Calendar/calendar.css
index 08d18d9..45caae6 100644
--- a/src/components/Calendar/calendar.css
+++ b/src/components/Calendar/calendar.css
@@ -1,123 +1,9 @@
-.calendar {
-
-}
-.select_date {
- display: flex;
- justify-content: center;
- padding: 15px;
-
-}
-
-.calendar_btn {
- padding: 5px 10px;
- text-align: center;
- color:white;
- font-size: 20px;
- border:none;
- background-color: inherit;
- cursor: pointer;
-}
-
-.calendar_select {
- text-align: center;
- color:white;
- font-size: 20px;
- border:none;
- background-color: inherit;
- cursor: pointer;
-}
-.calendar_select option {
- background-color: #2e2f36;
-}
-
-.calendar_select_year, .calendar_select_month {
- width: 140px;
- text-align: start;
- padding-left: 12px;
-}
-.today_btn {
- border-bottom: 1px solid gray;
- border-radius: 10px;
-}
-
-
-.calendar_select:focus, .calendar_btn:focus{
- outline: none;
-}
-
-.calendar_table {
+.calendar-table {
display: flex;
- flex-direction:column;
+ flex-direction: column;
width: 65%;
+ padding-bottom: 10px;
margin: auto;
-
-
-
-}
-
-.calendar_table_dates {
- width: 100%;
- display: grid;
- grid-template-columns: repeat(7,1fr);
- grid-template-rows: repeat(6,1fr) ;
- background-color: #404040;
- grid-gap: 1px;
- border: 1px solid #404040;
- margin: auto;
-}
-
-
-.dataItem {
- justify-items: end;
- display: grid;
- padding: 3px;
- color:white;
- background-color: #282B30;
- min-width: 40px;
- min-height: 50px;
- font-size: 10px;
- cursor: pointer;
-}
-
-.daysOfWeek {
- justify-items: end;
- display: grid;
- grid-template-columns: repeat(7,1fr);
- margin: auto;
- width: 100%;
-}
-
-.dayOfWeek_Item {
- min-width: 40px;
-
-}
-
-
-.today {
- display: grid;
- width: 15px;
- height: 15px;
- background-color: #EF4631;
- border-radius: 50%;
- justify-content: center;
- align-items: center;
}
-.selectedDay{
-border: 1px solid cornflowerblue;
-}
-
-.weekend {
- background-color: #2e2f36;
-}
-
-.notThisMonthColor {
- color: #53565D;
-}
-.haveTaskForThisDay {
- background-color: blueviolet;
- border-radius: 50%;
- width: 15px;
- height: 15px;
-}
\ No newline at end of file
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index 8675126..9d40dcf 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -1,11 +1,41 @@
-import React from 'react';
-
-const Header = () => {
- return (
-
-
To Do List
-
- );
-};
-
-export default Header;
\ No newline at end of file
+import './header.css'
+
+import React from 'react'
+
+import { Button } from '../UI/my-button/index'
+import deleteDayTasksSVG from '../../icon/deleteDayTasks.svg'
+import deleteAllTasksSVG from '../../icon/deleteAllTasks.svg'
+import { useDispatch } from 'react-redux'
+import { deleteAllTasks, deleteSelectedDayTasks } from '../../redux/actions/todos-actions'
+
+function Header() {
+ const dispatch = useDispatch()
+
+ return (
+
+
+
+
+
To Do List
+
+
+
+ )
+}
+
+export default React.memo(Header)
diff --git a/src/components/Header/header.css b/src/components/Header/header.css
new file mode 100644
index 0000000..f856924
--- /dev/null
+++ b/src/components/Header/header.css
@@ -0,0 +1,13 @@
+.header-container {
+display: flex;
+}
+.header-buttons {
+ border:none;
+ margin: 5px;
+}
+.header-title {
+ text-align: center;
+ font-size: 24px;
+ padding-top: 20px;
+ margin: 0 auto 10px;
+}
\ No newline at end of file
diff --git a/src/components/ListItem/ListItem.js b/src/components/ListItem/ListItem.js
deleted file mode 100644
index 2e60f36..0000000
--- a/src/components/ListItem/ListItem.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import React, { useState } from 'react';
-
-import Draggable from 'react-draggable';
-
-import iconCross from '../../icon/cross.svg'
-import { ReactComponent as Done } from '../../icon/done.svg'
-import iconEdit from '../../icon/edit.svg'
-import save from '../../icon/save.svg'
-
-import './listitemstyle.css'
-
-
-const ListItem = ({ todo, setToDo , setSelectedDay, selectedDay}) => {
-
- const [edit, setEdit] = useState(null)
- const [value, setValue] = useState('')
- selectedDay = selectedDay.format('DDMMYYYY')
-
- function deleteToDo(id) {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectedDay] = [...todo[selectedDay]].filter((obj) => obj.id !== id)
- setToDo(todoCopy);
- }
-
- function statusToDo(id) {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectedDay] = [...todo[selectedDay]].filter((obj) => {
- if (obj.id === id) {
- obj.status = !obj.status
- }
- return obj;
- })
- setToDo(todoCopy)
- }
-
- function editToDo(id, title) {
- setEdit(id)
- setValue(title)
- }
-
- function saveToDo(id) {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectedDay] = [...todo[selectedDay]].map((item) => {
- if (item.id === id) {
- item.title = value
- }
- return item;
- })
- setToDo(todoCopy);
- setEdit(null);
- }
-
- function updatePosition(data, index) {
- const todoCopy = JSON.parse(JSON.stringify(todo))
- todoCopy[selectedDay][index].defaultPos = { x: data.x, y: data.y }
- setToDo(todoCopy)
- }
-
-
-
-
- return (
-
-
- { todo[selectedDay] ?
- todo[selectedDay].map((item, index) => (
-
-
{
- updatePosition(data, index)
- } }
-
- >
-
- {
- edit === item.id ?
-
- { index + 1 }.
- setValue((e.target.value)) }
- value={ value }
-
- />
-
:
-
{ index + 1 }. { item.title }
- }
- {
-
- edit === item.id ?
-
-
-
:
-
-
-
-
-
-
-
-
-
- }
-
-
-
-
- ))
- :
-
- Tasks not found
-
- }
-
-
- );
-};
-
-export default ListItem;
\ No newline at end of file
diff --git a/src/components/ToDoList/ToDoItem/EditTask/EditTask.js b/src/components/ToDoList/ToDoItem/EditTask/EditTask.js
new file mode 100644
index 0000000..17dc246
--- /dev/null
+++ b/src/components/ToDoList/ToDoItem/EditTask/EditTask.js
@@ -0,0 +1,34 @@
+import React from 'react'
+import { Input } from '../../../UI/my-input/index'
+import { Button } from '../../../UI/my-button/index'
+import save from '../../../../icon/save.svg'
+import { useDispatch } from 'react-redux'
+import { updateTask } from '../../../../redux/actions/todos-actions'
+
+function EditTask({ value, item, index, setEditingId, onInputChange }) {
+ const dispatch = useDispatch()
+
+ function saveToDo(id, value) {
+ const newTitle = { title: value }
+ dispatch(updateTask(id, newTitle))
+ setEditingId(null)
+ }
+
+ function handlerTitleChange(e) {
+ onInputChange(e.target.value)
+ }
+
+ return (
+
+
+ {index + 1}.
+
+
+
+
+ )
+}
+
+export default React.memo(EditTask)
diff --git a/src/components/ToDoList/ToDoItem/EditTask/editTask.css b/src/components/ToDoList/ToDoItem/EditTask/editTask.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/ToDoList/ToDoItem/ShowTask/ShowTask.js b/src/components/ToDoList/ToDoItem/ShowTask/ShowTask.js
new file mode 100644
index 0000000..55f8087
--- /dev/null
+++ b/src/components/ToDoList/ToDoItem/ShowTask/ShowTask.js
@@ -0,0 +1,36 @@
+import React from 'react'
+
+import { Button } from '../../../UI/my-button/index'
+import iconCross from '../../../../icon/cross.svg'
+import iconEdit from '../../../../icon/edit.svg'
+import { ReactComponent as Done } from '../../../../icon/done.svg'
+import { useDispatch } from 'react-redux'
+import { deleteTask, updateTask } from '../../../../redux/actions/todos-actions'
+
+function ShowTask({ item, index, editToDo }) {
+ const dispatch = useDispatch()
+
+ return (
+
+
+ {index + 1}. {item.title}
+
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(ShowTask)
diff --git a/src/components/ToDoList/ToDoItem/ShowTask/showTask.css b/src/components/ToDoList/ToDoItem/ShowTask/showTask.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/ListItem/listitemstyle.css b/src/components/ToDoList/ToDoItem/ToDoItem.css
similarity index 56%
rename from src/components/ListItem/listitemstyle.css
rename to src/components/ToDoList/ToDoItem/ToDoItem.css
index 927dcda..2300a2e 100644
--- a/src/components/ListItem/listitemstyle.css
+++ b/src/components/ToDoList/ToDoItem/ToDoItem.css
@@ -1,41 +1,41 @@
-.list_item {
- display: flex;
- position: absolute;
- width: 300px;
- min-height: 60px;
- justify-content: space-between;
- align-items: center;
- color:black;
- font-weight: bold;
- padding:5px;
- border-radius: 10px;
- cursor: move;
- margin: auto;
-
-}
-.listitem_btn {
- background-color: inherit;
- border:none;
- cursor: pointer;
-
-}
-
-.list_item-input-change {
- margin-left: 3px;
- background-color: inherit;
- border:none;
- border-bottom: 1px solid black;
- font-weight: bold;
- color:black;
- outline:none;
- font-size:16px;
- font-family: 'Montserrat', sans-serif;
-}
-.list_item-title-nochange {
- font-size:16px;
-}
-
-.item_done {
- text-decoration: line-through;
-}
-
+.list-item {
+ display: flex;
+ position: absolute;
+ width: 300px;
+ min-height: 60px;
+ justify-content: space-between;
+ align-items: center;
+ color: black;
+ font-weight: bold;
+ padding: 5px;
+ border-radius: 10px;
+ cursor: move;
+ margin: auto;
+}
+
+.list-item-input-change {
+ margin-left: 3px;
+ background-color: inherit;
+ border: none;
+ border-bottom: 1px solid black;
+ font-weight: bold;
+ color: black;
+ outline: none;
+ font-size: 16px;
+ font-family: "Montserrat", sans-serif;
+}
+
+.list-item-title-nochange {
+ font-size: 16px;
+}
+
+.item-done {
+ text-decoration: line-through;
+}
+
+.list-item-button {
+ background-color: inherit;
+ border: none;
+ cursor: pointer;
+ padding: 1px 6px;
+}
\ No newline at end of file
diff --git a/src/components/ToDoList/ToDoItem/ToDoItem.js b/src/components/ToDoList/ToDoItem/ToDoItem.js
new file mode 100644
index 0000000..200d7f7
--- /dev/null
+++ b/src/components/ToDoList/ToDoItem/ToDoItem.js
@@ -0,0 +1,58 @@
+import './ToDoItem.css'
+
+import Draggable from 'react-draggable'
+import React, { useCallback, useState } from 'react'
+
+import EditTask from './EditTask/EditTask'
+import ShowTask from './ShowTask/ShowTask'
+import { useDispatch } from 'react-redux'
+import { updateTask } from '../../../redux/actions/todos-actions'
+
+function ToDoItem({ item, index }) {
+ const [editingId, setEditingId] = useState(null)
+ const [titleChangeInput, setTitleChangeInput] = useState('')
+
+ const dispatch = useDispatch()
+
+ const editToDo = useCallback((id, title) => {
+ setEditingId(id)
+ setTitleChangeInput(title)
+ }, [])
+
+ const handlerTitleChange = useCallback((newTitle) => {
+ setTitleChangeInput(newTitle)
+ }, [])
+
+ function onStopDraggable(e, data) {
+ dispatch(updateTask(item.id, { defaultPos: { x: data.x, y: data.y } }))
+ }
+
+ return (
+
+
{
+ onStopDraggable(e, data)
+ }}
+ >
+
+ {editingId === item.id ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
+
+export default React.memo(ToDoItem)
diff --git a/src/components/ToDoList/ToDoList.js b/src/components/ToDoList/ToDoList.js
new file mode 100644
index 0000000..c520264
--- /dev/null
+++ b/src/components/ToDoList/ToDoList.js
@@ -0,0 +1,23 @@
+import './ToDolist.css'
+
+import React from 'react'
+
+import ToDoItem from './ToDoItem/ToDoItem'
+import { useSelector } from 'react-redux'
+import { getSelectedDayTasks } from '../../redux/selectors/todo-selectors'
+
+function ToDoList() {
+ const selectedDayTasks = useSelector(getSelectedDayTasks)
+
+ return (
+
+ {selectedDayTasks?.length ? (
+ selectedDayTasks.map((item, index) =>
)
+ ) : (
+
Tasks not found
+ )}
+
+ )
+}
+
+export default React.memo(ToDoList)
diff --git a/src/components/ToDoList/ToDolist.css b/src/components/ToDoList/ToDolist.css
new file mode 100644
index 0000000..af12360
--- /dev/null
+++ b/src/components/ToDoList/ToDolist.css
@@ -0,0 +1,11 @@
+.draggable-element {
+ width: 300px;
+ min-height: 60px;
+ position: absolute;
+}
+
+.header-taskNotFound {
+ text-align: center;
+}
+
+
diff --git a/src/components/UI/my-button/index.js b/src/components/UI/my-button/index.js
new file mode 100644
index 0000000..9c7dc58
--- /dev/null
+++ b/src/components/UI/my-button/index.js
@@ -0,0 +1,27 @@
+import './myButton.css'
+
+import React from 'react'
+import ReactTooltip from 'react-tooltip'
+
+export const Button = (props) => {
+ const classNames = 'myButton-default-style ' + props.className
+
+ if (props.tooltip) {
+ const tooltipType = props['tooltip-type'] ? props['tooltip-type'] : 'error'
+
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+}
diff --git a/src/components/UI/my-button/myButton.css b/src/components/UI/my-button/myButton.css
new file mode 100644
index 0000000..07c3743
--- /dev/null
+++ b/src/components/UI/my-button/myButton.css
@@ -0,0 +1,11 @@
+.myButton-default-style {
+ padding: 5px 10px;
+ text-align: center;
+ color: white;
+ background-color: inherit;
+ cursor: pointer;
+}
+button:disabled {
+ opacity: 50%;
+ cursor: default;
+}
\ No newline at end of file
diff --git a/src/components/UI/my-input/index.js b/src/components/UI/my-input/index.js
new file mode 100644
index 0000000..79a1aae
--- /dev/null
+++ b/src/components/UI/my-input/index.js
@@ -0,0 +1,13 @@
+import './myInput.css'
+
+import React from 'react'
+
+export const Input = (props) => {
+ const classNames = 'myInput-default-style ' + props.className
+
+ return (
+
+ {props.children}
+
+ )
+}
diff --git a/src/components/UI/my-input/myInput.css b/src/components/UI/my-input/myInput.css
new file mode 100644
index 0000000..2664858
--- /dev/null
+++ b/src/components/UI/my-input/myInput.css
@@ -0,0 +1,7 @@
+.myInput-default-style {
+ background-color: inherit;
+ border: none;
+ border-bottom: 1px solid white;
+ color: white;
+ outline: none;
+}
\ No newline at end of file
diff --git a/src/components/UI/my-select/index.js b/src/components/UI/my-select/index.js
new file mode 100644
index 0000000..9dad99c
--- /dev/null
+++ b/src/components/UI/my-select/index.js
@@ -0,0 +1,21 @@
+import './mySelect.css'
+
+import React from 'react'
+
+export const Select = ({ optionsObj, children, ...props }) => {
+ const classNames = 'mySelect-default-style ' + props.className
+
+ return (
+
+ )
+}
diff --git a/src/components/UI/my-select/mySelect.css b/src/components/UI/my-select/mySelect.css
new file mode 100644
index 0000000..6a364dc
--- /dev/null
+++ b/src/components/UI/my-select/mySelect.css
@@ -0,0 +1,10 @@
+.mySelect-default-style {
+ text-align: start;
+ color: white;
+ border: none;
+ background-color: inherit;
+ cursor: pointer;
+}
+.mySelect-default-style option {
+ background-color: #2e2f36;
+}
\ No newline at end of file
diff --git a/src/components/utils/data.js b/src/components/utils/data.js
new file mode 100644
index 0000000..00576a1
--- /dev/null
+++ b/src/components/utils/data.js
@@ -0,0 +1,111 @@
+import moment from 'moment'
+
+export const monthNamesArr = moment()._locale._months
+
+export function isSelectedDay(dayObj, selectedDay) {
+ return dayObj.id === selectedDay
+}
+export function startWeekFromMonday() {
+ moment.updateLocale('en', { week: { dow: 1 } })
+}
+
+export function getTodayDayId() {
+ return moment().format('DDMMYYYY')
+}
+
+export function getCurrentYear() {
+ return moment().year()
+}
+
+export function getCurrentMonth() {
+ return moment().month()
+}
+
+export const isWeekend = (momentDay) => {
+ return momentDay.isoWeekday() === 6 || momentDay.isoWeekday() === 7
+}
+
+export function getDayOfMonth(momentDay) {
+ return momentDay.date()
+}
+
+export function getMonthOfYear(momentDay) {
+ return momentDay.month() + 1
+}
+
+export function getYear(momentDay) {
+ return momentDay.year()
+}
+
+export function isCurrentMonth(momentDay, selectMonth) {
+ return momentDay.month() === selectMonth
+}
+
+export function getId(momentDay) {
+ return momentDay.format('DDMMYYYY')
+}
+
+export function isToday(momentDay) {
+ return momentDay.isSame(moment(), 'day')
+}
+
+export function createOneDay(newDay, selectMonthValue) {
+ return {
+ id: getId(newDay),
+ date: {
+ day: getDayOfMonth(newDay),
+ month: getMonthOfYear(newDay),
+ year: getYear(newDay),
+ },
+ isWeekend: isWeekend(newDay),
+ isCurrentMonth: isCurrentMonth(newDay, selectMonthValue),
+ isToday: isToday(newDay),
+ }
+}
+
+export function createAllDaysForCurrWindow(year, month, day = 1) {
+ const selectedDay = moment().set({ year: year, month: month, date: day })
+ const startDay = selectedDay.clone().startOf('month').startOf('week')
+ const currDay = startDay.subtract(1, 'day').clone()
+ const resultArrAllDays = [...Array(42)].map(() => createOneDay(currDay.add(1, 'day').clone(), month))
+
+ return resultArrAllDays
+}
+
+export const yearsValues = {
+ 2016: 2016,
+ 2017: 2017,
+ 2018: 2018,
+ 2019: 2019,
+ 2020: 2020,
+ 2021: 2021,
+ 2022: 2022,
+ 2023: 2023,
+ 2024: 2024,
+ 2025: 2025,
+ 2026: 2026,
+}
+
+// export const createDayMetods = {
+// getMonthOfYear: function getMonthOfYear(momentDay) {
+// return momentDay.month() + 1
+// },
+// getYear: function getYear(momentDay) {
+// return momentDay.year()
+// },
+// isCurrentMonth: function isCurrentMonth(momentDay, selectMonth) {
+// return momentDay.month() === selectMonth
+// },
+// getId: function getId(momentDay) {
+// return momentDay.format('DDMMYYYY')
+// },
+// isToday: function isToday(momentDay) {
+// return momentDay.isSame(moment(), 'day')
+// },
+// isWeekend: function isWeekend(momentDay) {
+// return momentDay.isoWeekday() === 6 || momentDay.isoWeekday() === 7
+// },
+// getDayOfMonth: function getDayOfMonth(momentDay) {
+// return momentDay.date()
+// },
+// }
diff --git a/src/constants/actions-types/calendar.js b/src/constants/actions-types/calendar.js
new file mode 100644
index 0000000..b5e70ad
--- /dev/null
+++ b/src/constants/actions-types/calendar.js
@@ -0,0 +1,3 @@
+export const CHANGE_SELECT_MONTH = 'CHANGE_SELECT_MONTH'
+export const CHANGE_SELECT_YEAR = 'CHANGE_SELECT_YEAR'
+export const CHANGE_WINDOW_CALENDAR = 'CHANGE_WINDOW_CALENDAR'
diff --git a/src/constants/actions-types/index.js b/src/constants/actions-types/index.js
new file mode 100644
index 0000000..a8f6238
--- /dev/null
+++ b/src/constants/actions-types/index.js
@@ -0,0 +1,2 @@
+export * from './todos'
+export * from './calendar'
diff --git a/src/constants/actions-types/todos.js b/src/constants/actions-types/todos.js
new file mode 100644
index 0000000..c9624e4
--- /dev/null
+++ b/src/constants/actions-types/todos.js
@@ -0,0 +1,8 @@
+export const ADD_TASK = 'ADD_TASK'
+export const DELETE_TASK = 'DELETE_TASK'
+export const UPDATE_TASK = 'UPDATE_TASK'
+
+export const DELETE_SELECTED_DAY_TASKS = 'DELETE_SELECTED_DAY_TASKS'
+export const DELETE_ALL_TASKS = 'DELETE_ALL_TASKS'
+
+export const CHANGE_SELECTED_DAY = 'CHANGE_SELECTED_DAY'
diff --git a/src/constants/constants.js b/src/constants/constants.js
new file mode 100644
index 0000000..b1c097c
--- /dev/null
+++ b/src/constants/constants.js
@@ -0,0 +1,8 @@
+export const keyCode = {
+ ENTER: 13,
+ TAB: 9,
+}
+export const fixSize = {
+ WIDTH_HEADER: 300,
+ WIDTH_CALENDAR: 580,
+}
diff --git a/src/icon/deleteAllTasks.svg b/src/icon/deleteAllTasks.svg
new file mode 100644
index 0000000..647c816
--- /dev/null
+++ b/src/icon/deleteAllTasks.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icon/deleteDayTasks.svg b/src/icon/deleteDayTasks.svg
new file mode 100644
index 0000000..90e2394
--- /dev/null
+++ b/src/icon/deleteDayTasks.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index e1e164e..c900898 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,10 +1,23 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import App from './App';
-
-
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render(
-
-);
-
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App'
+
+import thunk from 'redux-thunk'
+import { createStore, compose, applyMiddleware } from 'redux'
+import { rootReducer } from './redux/reducers'
+import { Provider } from 'react-redux'
+
+const store = createStore(
+ rootReducer,
+ compose(
+ applyMiddleware(thunk),
+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), //<-- для redux devTools`a
+ ),
+)
+
+const root = ReactDOM.createRoot(document.getElementById('root'))
+root.render(
+
+
+ ,
+)
diff --git a/src/redux/actions/calendar-actions.js b/src/redux/actions/calendar-actions.js
new file mode 100644
index 0000000..068c13f
--- /dev/null
+++ b/src/redux/actions/calendar-actions.js
@@ -0,0 +1,22 @@
+import { CHANGE_SELECT_MONTH, CHANGE_SELECT_YEAR, CHANGE_WINDOW_CALENDAR } from '../../constants/actions-types/'
+
+export function changeSelectMonth(newMonth) {
+ return {
+ type: CHANGE_SELECT_MONTH,
+ data: newMonth,
+ }
+}
+
+export function changeSelectYear(newYear) {
+ return {
+ type: CHANGE_SELECT_YEAR,
+ data: newYear,
+ }
+}
+
+export function changeWindowCalendar(newWindowCalendar) {
+ return {
+ type: CHANGE_WINDOW_CALENDAR,
+ data: newWindowCalendar,
+ }
+}
diff --git a/src/redux/actions/todos-actions.js b/src/redux/actions/todos-actions.js
new file mode 100644
index 0000000..ca4513a
--- /dev/null
+++ b/src/redux/actions/todos-actions.js
@@ -0,0 +1,48 @@
+import {
+ ADD_TASK,
+ CHANGE_SELECTED_DAY,
+ DELETE_ALL_TASKS,
+ DELETE_SELECTED_DAY_TASKS,
+ DELETE_TASK,
+ UPDATE_TASK,
+} from '../../constants/actions-types/'
+
+export function addTask(newTask) {
+ return {
+ type: ADD_TASK,
+ data: { newTask },
+ }
+}
+
+export function deleteTask(task) {
+ return {
+ type: DELETE_TASK,
+ data: task,
+ }
+}
+
+export function updateTask(taskId, changes) {
+ return {
+ type: UPDATE_TASK,
+ data: { taskId, changes },
+ }
+}
+
+export function changeSelectedDay(date) {
+ return {
+ type: CHANGE_SELECTED_DAY,
+ data: date,
+ }
+}
+
+export function deleteSelectedDayTasks() {
+ return {
+ type: DELETE_SELECTED_DAY_TASKS,
+ }
+}
+
+export function deleteAllTasks() {
+ return {
+ type: DELETE_ALL_TASKS,
+ }
+}
diff --git a/src/redux/reducers/calendar/index.js b/src/redux/reducers/calendar/index.js
new file mode 100644
index 0000000..b63f1a1
--- /dev/null
+++ b/src/redux/reducers/calendar/index.js
@@ -0,0 +1,24 @@
+import * as DateUtils from '../../../components/utils/data'
+import { CHANGE_SELECT_MONTH, CHANGE_SELECT_YEAR, CHANGE_WINDOW_CALENDAR } from '../../../constants/actions-types'
+
+const initialState = {
+ selectMonthValue: DateUtils.getCurrentMonth(),
+ selectYearValue: DateUtils.getCurrentYear(),
+ currentWindowCalendar: [],
+}
+
+export const calendarReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case CHANGE_SELECT_MONTH:
+ return { ...state, selectMonthValue: action.data }
+
+ case CHANGE_SELECT_YEAR:
+ return { ...state, selectYearValue: action.data }
+
+ case CHANGE_WINDOW_CALENDAR:
+ return { ...state, currentWindowCalendar: action.data }
+
+ default:
+ return state
+ }
+}
diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js
new file mode 100644
index 0000000..a92cc9b
--- /dev/null
+++ b/src/redux/reducers/index.js
@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux'
+import { todosReducer } from './todos'
+import { calendarReducer } from './calendar'
+
+export const rootReducer = combineReducers({
+ todosReducer,
+ calendarReducer,
+})
diff --git a/src/redux/reducers/todos/index.js b/src/redux/reducers/todos/index.js
new file mode 100644
index 0000000..c72fbe2
--- /dev/null
+++ b/src/redux/reducers/todos/index.js
@@ -0,0 +1,62 @@
+import {
+ ADD_TASK,
+ CHANGE_SELECTED_DAY,
+ DELETE_ALL_TASKS,
+ DELETE_SELECTED_DAY_TASKS,
+ DELETE_TASK,
+ UPDATE_TASK,
+} from '../../../constants/actions-types'
+import { getTodayDayId } from '../../../components/utils/data'
+
+const initialState = {
+ todo: JSON.parse(localStorage.getItem('items')) || {},
+ selectedDay: getTodayDayId(),
+}
+
+export const todosReducer = (state = initialState, action) => {
+ const { todo, selectedDay } = state
+ switch (action.type) {
+ case ADD_TASK: {
+ const { newTask } = action.data
+ const selectedDayTasks = todo[selectedDay]
+ const newDayTask = selectedDayTasks ? [...selectedDayTasks, newTask] : [newTask]
+
+ return { ...state, todo: { ...todo, [selectedDay]: newDayTask } }
+ }
+
+ case DELETE_TASK: {
+ const task = action.data
+ const newSelectDayToDo = todo[selectedDay].filter(({ id }) => {
+ return id !== task.id
+ })
+
+ return { ...state, todo: { ...todo, [selectedDay]: newSelectDayToDo } }
+ }
+
+ case UPDATE_TASK: {
+ const { taskId, changes } = action.data
+ const newSelectedDayToDo = todo[selectedDay].map((task) => {
+ if (task.id !== taskId) {
+ return task
+ }
+ return { ...task, ...changes }
+ })
+ return { ...state, todo: { ...todo, [selectedDay]: newSelectedDayToDo } }
+ }
+
+ case DELETE_SELECTED_DAY_TASKS: {
+ return { ...state, todo: { ...todo, [selectedDay]: {} } }
+ }
+
+ case DELETE_ALL_TASKS: {
+ return { ...state, todo: {} }
+ }
+
+ case CHANGE_SELECTED_DAY: {
+ return { ...state, selectedDay: action.data }
+ }
+
+ default:
+ return state
+ }
+}
diff --git a/src/redux/selectors/calendar-selectors.js b/src/redux/selectors/calendar-selectors.js
new file mode 100644
index 0000000..791fcb2
--- /dev/null
+++ b/src/redux/selectors/calendar-selectors.js
@@ -0,0 +1,10 @@
+export const getSelectMonth = (state) => state.calendarReducer.selectMonthValue
+export const getSelectYear = (state) => state.calendarReducer.selectYearValue
+export const getWindowCalendar = (state) => state.calendarReducer.currentWindowCalendar
+export const mapStateCalendarToProps = (state) => {
+ return {
+ selectMonthValue: getSelectMonth(state),
+ selectYearValue: getSelectYear(state),
+ currentWindowCalendar: getWindowCalendar(state),
+ }
+}
diff --git a/src/redux/selectors/todo-selectors.js b/src/redux/selectors/todo-selectors.js
new file mode 100644
index 0000000..4b4228e
--- /dev/null
+++ b/src/redux/selectors/todo-selectors.js
@@ -0,0 +1,6 @@
+export const getTodo = (state) => state.todosReducer.todo
+export const getSelectedDay = (state) => state.todosReducer.selectedDay
+export const getSelectedDayTasks = (state) => getTodo(state)[getSelectedDay(state)]
+export const mapStateToDoToProps = (state) => {
+ return { todo: getTodo(state), selectedDay: getSelectedDay(state) }
+}