HyeonJun Jeon

Merge branch 'mysql' into 'master'

...@@ -14,10 +14,12 @@ ...@@ -14,10 +14,12 @@
14 "axios": "^0.27.2", 14 "axios": "^0.27.2",
15 "body-parser": "^1.20.0", 15 "body-parser": "^1.20.0",
16 "cors": "^2.8.5", 16 "cors": "^2.8.5",
17 + "crypto-js": "^4.1.1",
17 "express": "^4.18.1", 18 "express": "^4.18.1",
18 "fs": "^0.0.1-security", 19 "fs": "^0.0.1-security",
19 "localforage": "^1.10.0", 20 "localforage": "^1.10.0",
20 "mysql": "^2.18.1", 21 "mysql": "^2.18.1",
22 + "mysql2": "^2.3.3",
21 "puppeteer": "^14.1.1", 23 "puppeteer": "^14.1.1",
22 "react": "^18.1.0", 24 "react": "^18.1.0",
23 "react-dom": "^18.1.0", 25 "react-dom": "^18.1.0",
...@@ -5627,6 +5629,11 @@ ...@@ -5627,6 +5629,11 @@
5627 "node": ">= 8" 5629 "node": ">= 8"
5628 } 5630 }
5629 }, 5631 },
5632 + "node_modules/crypto-js": {
5633 + "version": "4.1.1",
5634 + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
5635 + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
5636 + },
5630 "node_modules/crypto-random-string": { 5637 "node_modules/crypto-random-string": {
5631 "version": "2.0.0", 5638 "version": "2.0.0",
5632 "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", 5639 "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
...@@ -6154,6 +6161,14 @@ ...@@ -6154,6 +6161,14 @@
6154 "node": ">=0.4.0" 6161 "node": ">=0.4.0"
6155 } 6162 }
6156 }, 6163 },
6164 + "node_modules/denque": {
6165 + "version": "2.0.1",
6166 + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
6167 + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==",
6168 + "engines": {
6169 + "node": ">=0.10"
6170 + }
6171 + },
6157 "node_modules/depd": { 6172 "node_modules/depd": {
6158 "version": "2.0.0", 6173 "version": "2.0.0",
6159 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 6174 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
...@@ -7993,6 +8008,14 @@ ...@@ -7993,6 +8008,14 @@
7993 "url": "https://github.com/sponsors/ljharb" 8008 "url": "https://github.com/sponsors/ljharb"
7994 } 8009 }
7995 }, 8010 },
8011 + "node_modules/generate-function": {
8012 + "version": "2.3.1",
8013 + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
8014 + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
8015 + "dependencies": {
8016 + "is-property": "^1.0.2"
8017 + }
8018 + },
7996 "node_modules/gensync": { 8019 "node_modules/gensync": {
7997 "version": "1.0.0-beta.2", 8020 "version": "1.0.0-beta.2",
7998 "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 8021 "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
...@@ -8847,6 +8870,11 @@ ...@@ -8847,6 +8870,11 @@
8847 "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", 8870 "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
8848 "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" 8871 "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
8849 }, 8872 },
8873 + "node_modules/is-property": {
8874 + "version": "1.0.2",
8875 + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
8876 + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
8877 + },
8850 "node_modules/is-regex": { 8878 "node_modules/is-regex": {
8851 "version": "1.1.4", 8879 "version": "1.1.4",
8852 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 8880 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
...@@ -11305,6 +11333,11 @@ ...@@ -11305,6 +11333,11 @@
11305 "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", 11333 "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
11306 "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 11334 "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
11307 }, 11335 },
11336 + "node_modules/long": {
11337 + "version": "4.0.0",
11338 + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
11339 + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
11340 + },
11308 "node_modules/loose-envify": { 11341 "node_modules/loose-envify": {
11309 "version": "1.4.0", 11342 "version": "1.4.0",
11310 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 11343 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
...@@ -11646,6 +11679,57 @@ ...@@ -11646,6 +11679,57 @@
11646 "safe-buffer": "~5.1.0" 11679 "safe-buffer": "~5.1.0"
11647 } 11680 }
11648 }, 11681 },
11682 + "node_modules/mysql2": {
11683 + "version": "2.3.3",
11684 + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
11685 + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
11686 + "dependencies": {
11687 + "denque": "^2.0.1",
11688 + "generate-function": "^2.3.1",
11689 + "iconv-lite": "^0.6.3",
11690 + "long": "^4.0.0",
11691 + "lru-cache": "^6.0.0",
11692 + "named-placeholders": "^1.1.2",
11693 + "seq-queue": "^0.0.5",
11694 + "sqlstring": "^2.3.2"
11695 + },
11696 + "engines": {
11697 + "node": ">= 8.0"
11698 + }
11699 + },
11700 + "node_modules/mysql2/node_modules/sqlstring": {
11701 + "version": "2.3.3",
11702 + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
11703 + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
11704 + "engines": {
11705 + "node": ">= 0.6"
11706 + }
11707 + },
11708 + "node_modules/named-placeholders": {
11709 + "version": "1.1.2",
11710 + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
11711 + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
11712 + "dependencies": {
11713 + "lru-cache": "^4.1.3"
11714 + },
11715 + "engines": {
11716 + "node": ">=6.0.0"
11717 + }
11718 + },
11719 + "node_modules/named-placeholders/node_modules/lru-cache": {
11720 + "version": "4.1.5",
11721 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
11722 + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
11723 + "dependencies": {
11724 + "pseudomap": "^1.0.2",
11725 + "yallist": "^2.1.2"
11726 + }
11727 + },
11728 + "node_modules/named-placeholders/node_modules/yallist": {
11729 + "version": "2.1.2",
11730 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
11731 + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
11732 + },
11649 "node_modules/nanoid": { 11733 "node_modules/nanoid": {
11650 "version": "3.3.4", 11734 "version": "3.3.4",
11651 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 11735 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
...@@ -13507,6 +13591,11 @@ ...@@ -13507,6 +13591,11 @@
13507 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 13591 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
13508 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 13592 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
13509 }, 13593 },
13594 + "node_modules/pseudomap": {
13595 + "version": "1.0.2",
13596 + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
13597 + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
13598 + },
13510 "node_modules/psl": { 13599 "node_modules/psl": {
13511 "version": "1.8.0", 13600 "version": "1.8.0",
13512 "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 13601 "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
...@@ -14536,6 +14625,11 @@ ...@@ -14536,6 +14625,11 @@
14536 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 14625 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
14537 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 14626 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
14538 }, 14627 },
14628 + "node_modules/seq-queue": {
14629 + "version": "0.0.5",
14630 + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
14631 + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
14632 + },
14539 "node_modules/serialize-javascript": { 14633 "node_modules/serialize-javascript": {
14540 "version": "6.0.0", 14634 "version": "6.0.0",
14541 "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 14635 "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
...@@ -20713,6 +20807,11 @@ ...@@ -20713,6 +20807,11 @@
20713 "which": "^2.0.1" 20807 "which": "^2.0.1"
20714 } 20808 }
20715 }, 20809 },
20810 + "crypto-js": {
20811 + "version": "4.1.1",
20812 + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
20813 + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
20814 + },
20716 "crypto-random-string": { 20815 "crypto-random-string": {
20717 "version": "2.0.0", 20816 "version": "2.0.0",
20718 "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", 20817 "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
...@@ -21072,6 +21171,11 @@ ...@@ -21072,6 +21171,11 @@
21072 "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 21171 "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
21073 "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 21172 "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
21074 }, 21173 },
21174 + "denque": {
21175 + "version": "2.0.1",
21176 + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
21177 + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
21178 + },
21075 "depd": { 21179 "depd": {
21076 "version": "2.0.0", 21180 "version": "2.0.0",
21077 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 21181 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
...@@ -22417,6 +22521,14 @@ ...@@ -22417,6 +22521,14 @@
22417 "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 22521 "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
22418 "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" 22522 "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
22419 }, 22523 },
22524 + "generate-function": {
22525 + "version": "2.3.1",
22526 + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
22527 + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
22528 + "requires": {
22529 + "is-property": "^1.0.2"
22530 + }
22531 + },
22420 "gensync": { 22532 "gensync": {
22421 "version": "1.0.0-beta.2", 22533 "version": "1.0.0-beta.2",
22422 "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 22534 "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
...@@ -23004,6 +23116,11 @@ ...@@ -23004,6 +23116,11 @@
23004 "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", 23116 "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
23005 "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" 23117 "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
23006 }, 23118 },
23119 + "is-property": {
23120 + "version": "1.0.2",
23121 + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
23122 + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
23123 + },
23007 "is-regex": { 23124 "is-regex": {
23008 "version": "1.1.4", 23125 "version": "1.1.4",
23009 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 23126 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
...@@ -24807,6 +24924,11 @@ ...@@ -24807,6 +24924,11 @@
24807 "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", 24924 "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
24808 "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 24925 "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
24809 }, 24926 },
24927 + "long": {
24928 + "version": "4.0.0",
24929 + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
24930 + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
24931 + },
24810 "loose-envify": { 24932 "loose-envify": {
24811 "version": "1.4.0", 24933 "version": "1.4.0",
24812 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 24934 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
...@@ -25067,6 +25189,52 @@ ...@@ -25067,6 +25189,52 @@
25067 } 25189 }
25068 } 25190 }
25069 }, 25191 },
25192 + "mysql2": {
25193 + "version": "2.3.3",
25194 + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
25195 + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
25196 + "requires": {
25197 + "denque": "^2.0.1",
25198 + "generate-function": "^2.3.1",
25199 + "iconv-lite": "^0.6.3",
25200 + "long": "^4.0.0",
25201 + "lru-cache": "^6.0.0",
25202 + "named-placeholders": "^1.1.2",
25203 + "seq-queue": "^0.0.5",
25204 + "sqlstring": "^2.3.2"
25205 + },
25206 + "dependencies": {
25207 + "sqlstring": {
25208 + "version": "2.3.3",
25209 + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
25210 + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="
25211 + }
25212 + }
25213 + },
25214 + "named-placeholders": {
25215 + "version": "1.1.2",
25216 + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
25217 + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
25218 + "requires": {
25219 + "lru-cache": "^4.1.3"
25220 + },
25221 + "dependencies": {
25222 + "lru-cache": {
25223 + "version": "4.1.5",
25224 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
25225 + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
25226 + "requires": {
25227 + "pseudomap": "^1.0.2",
25228 + "yallist": "^2.1.2"
25229 + }
25230 + },
25231 + "yallist": {
25232 + "version": "2.1.2",
25233 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
25234 + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
25235 + }
25236 + }
25237 + },
25070 "nanoid": { 25238 "nanoid": {
25071 "version": "3.3.4", 25239 "version": "3.3.4",
25072 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 25240 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
...@@ -26271,6 +26439,11 @@ ...@@ -26271,6 +26439,11 @@
26271 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 26439 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
26272 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 26440 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
26273 }, 26441 },
26442 + "pseudomap": {
26443 + "version": "1.0.2",
26444 + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
26445 + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
26446 + },
26274 "psl": { 26447 "psl": {
26275 "version": "1.8.0", 26448 "version": "1.8.0",
26276 "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 26449 "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
...@@ -27003,6 +27176,11 @@ ...@@ -27003,6 +27176,11 @@
27003 } 27176 }
27004 } 27177 }
27005 }, 27178 },
27179 + "seq-queue": {
27180 + "version": "0.0.5",
27181 + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
27182 + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
27183 + },
27006 "serialize-javascript": { 27184 "serialize-javascript": {
27007 "version": "6.0.0", 27185 "version": "6.0.0",
27008 "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 27186 "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
......
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
9 "axios": "^0.27.2", 9 "axios": "^0.27.2",
10 "body-parser": "^1.20.0", 10 "body-parser": "^1.20.0",
11 "cors": "^2.8.5", 11 "cors": "^2.8.5",
12 + "crypto-js": "^4.1.1",
12 "express": "^4.18.1", 13 "express": "^4.18.1",
13 "fs": "^0.0.1-security", 14 "fs": "^0.0.1-security",
14 "localforage": "^1.10.0", 15 "localforage": "^1.10.0",
15 "mysql": "^2.18.1", 16 "mysql": "^2.18.1",
17 + "mysql2": "^2.3.3",
16 "puppeteer": "^14.1.1", 18 "puppeteer": "^14.1.1",
17 "react": "^18.1.0", 19 "react": "^18.1.0",
18 "react-dom": "^18.1.0", 20 "react-dom": "^18.1.0",
......
...@@ -55,6 +55,7 @@ function process(sche, k, v) { ...@@ -55,6 +55,7 @@ function process(sche, k, v) {
55 case "URL": 55 case "URL":
56 k = "url"; 56 k = "url";
57 sche.subjectID = v.substring(v.indexOf("se_") + 3, v.indexOf("&m")); 57 sche.subjectID = v.substring(v.indexOf("se_") + 3, v.indexOf("&m"));
58 + v = `https://khcanvas.khu.ac.kr/courses/${sche.subjectID}/assignments/${sche.uid}`;
58 break; 59 break;
59 default: 60 default:
60 errArr.push(k); 61 errArr.push(k);
......
1 const mysql = require("mysql"); 1 const mysql = require("mysql");
2 +const mysql2 = require("mysql2/promise");
2 const fs = require("fs"); 3 const fs = require("fs");
3 const parseICal = require("./ICal"); 4 const parseICal = require("./ICal");
4 5
...@@ -61,8 +62,19 @@ function jcalToSQL(jcal, userID) { ...@@ -61,8 +62,19 @@ function jcalToSQL(jcal, userID) {
61 62
62 // iCal의 소유주를 DB에 등록 후 userID get 63 // iCal의 소유주를 DB에 등록 후 userID get
63 // subjectID가 subjects, userID가 users에 있어야함 64 // subjectID가 subjects, userID가 users에 있어야함
64 -const fdata = fs.readFileSync("C:/Users/teddy/Downloads/data.ics", "utf8");
65 -const jcal = parseICal(fdata);
66 -jcalToSQL(jcal, 1);
67 65
68 -module.exports = jcalToSQL; 66 +// const fdata = fs.readFileSync("C:/Users/teddy/Downloads/data.ics", "utf8");
67 +// const jcal = parseICal(fdata);
68 +// jcalToSQL(jcal, 1);
69 +
70 +const [id, pw] = fs
71 + .readFileSync("server/libs/sql.pvdata", "utf8")
72 + .split("\r\n");
73 +const connectOption = {
74 + host: "localhost",
75 + user: id,
76 + password: pw,
77 + database: "db",
78 +};
79 +
80 +module.exports = { jcalToSQL, connectOption };
......
1 const express = require("express"); 1 const express = require("express");
2 -const mysql = require("mysql"); 2 +const mysql2 = require("mysql2/promise");
3 -const fs = require("fs"); 3 +const { connectOption } = require("../libs/MySQL");
4 -const router = express.Router(); 4 +const dbRouter = express.Router();
5 5
6 -const [id, pw] = fs 6 +const schedules_dateRouter = require("./schedules_date");
7 - .readFileSync("server/libs/sql.pvdata", "utf8") 7 +const schedules_repeatRouter = require("./Schedules_repeat");
8 - .split("\r\n"); 8 +const schedules_timeRouter = require("./Schedules_time");
9 +const subjectsRouter = require("./Subjects");
10 +const userSubjectRouter = require("./user-subject");
11 +const usersRouter = require("./Users");
9 12
10 -const connection = mysql.createConnection({ 13 +async function route() {
11 - host: "localhost", 14 + const connection = await mysql2.createConnection(connectOption);
12 - user: id,
13 - password: pw,
14 - database: "mydb",
15 -});
16 15
17 -router.get("/", (req, res) => { 16 + // (userID, uid)
18 - res.send("DB Root"); 17 + dbRouter.delete("/schedule", async (req, res) => {
19 -}); 18 + console.log("delete /db/schedule");
20 - 19 + try {
21 -router.get("/mytable", (req, res) => { 20 + const queryString = `
22 - connection.query("SELECT * from mytable", (error, rows) => { 21 + DELETE FROM \`${req.body.table}\` sc
23 - if (error) throw error; 22 + WHERE sc.userID = ${req.body.userID}
24 - console.log(rows); 23 + AND sc.uid = ${req.body.uid}`;
25 - res.send(rows); 24 + await connection.query(queryString);
25 + res.end();
26 + } catch (e) {
27 + console.log(e);
28 + res.end();
29 + }
26 }); 30 });
27 -});
28 31
29 -module.exports = router; 32 + dbRouter.use("/user-subject", userSubjectRouter);
33 + dbRouter.use("/users", usersRouter);
34 + dbRouter.use("/subjects", subjectsRouter);
35 + dbRouter.use("/schedules_date", schedules_dateRouter);
36 + dbRouter.use("/schedules_time", schedules_timeRouter);
37 + dbRouter.use("/schedules_repeat", schedules_repeatRouter);
38 +}
39 +
40 +route();
41 +
42 +module.exports = dbRouter;
......
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_dateRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, date) => schedules
10 + schedules_dateRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_date");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail,
15 + sbj.name, us.nickname, us.color, sc.uid, "schedules_date" \`table\`
16 + FROM schedules_date sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.date = "${req.query.date}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1`;
26 + const [results] = await connection.query(queryString);
27 + res.send(results);
28 + } catch (e) {
29 + console.log(e);
30 + res.end();
31 + }
32 + });
33 +
34 + // (userID, label, subjectID, type, description, url, date)
35 + schedules_dateRouter.post("/", async (req, res) => {
36 + console.log("post /db/schedules_date");
37 + try {
38 + let queryString = `
39 + INSERT INTO \`schedules_date\`
40 + (userID, label, subjectID, type, description, url, date, status)
41 + VALUES (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}",
42 + "${req.body.description}", "${req.body.url}", "${req.body.date}", 1);`;
43 + await connection.query(queryString);
44 +
45 + queryString = `UPDATE \`schedules_date\`
46 + SET uid = -ID
47 + WHERE ID = LAST_INSERT_ID();`;
48 + await connection.query(queryString);
49 +
50 + queryString = `
51 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail,
52 + sbj.name, us.nickname, us.color, sc.uid, "schedules_date" \`table\`
53 + FROM schedules_date sc
54 + INNER JOIN \`user-subject\` us
55 + ON sc.userID = us.userID
56 + AND sc.subjectID = us.subjectID
57 + AND us.status = 1
58 + INNER JOIN subjects sbj
59 + ON sc.subjectID = sbj.ID
60 + WHERE sc.ID = LAST_INSERT_ID()`;
61 + [results] = await connection.query(queryString);
62 + res.send(results[0]);
63 + } catch (e) {
64 + console.log(e);
65 + res.end();
66 + }
67 + });
68 +}
69 +route();
70 +
71 +module.exports = schedules_dateRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_repeatRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, day) => schedules
10 + schedules_repeatRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_repeat");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.uid, sc.url, sc.detail, sbj.name,
15 + us.nickname, us.color, sc.startTime, sc.endTime, "schedules_repeat" \`table\`
16 + FROM schedules_repeat sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.day = "${req.query.day}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1
26 + ORDER BY sc.startTime`;
27 + const [results] = await connection.query(queryString);
28 + res.send(results);
29 + } catch (e) {
30 + console.log(e);
31 + res.end();
32 + }
33 + });
34 +
35 + // (userID, label, subjectID, type, desciption, url, status, day, startTime, endTime)
36 + schedules_repeatRouter.post("/", async (req, res) => {
37 + console.log("/db/schedules_repeat");
38 + try {
39 + const queryString = `
40 + INSERT INTO schedules_repeat (userID, label, subjectID, type, status, day, startTime, endTime)
41 + VALUE (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}",
42 + 1, ${req.body.day}, "${req.body.startTime}", "${req.body.endTime}");`;
43 +
44 + queryString = `
45 + UPDATE schedules_repeat
46 + SET uid = -ID
47 + WHERE ID = LAST_INSERT_ID()`;
48 + await connection.query(queryString);
49 +
50 + queryString = `
51 + SELECT sc.label, sc.type, sc.description, sc.uid, sc.url, sc.detail, sbj.name,
52 + us.nickname, us.color, sc.startTime, sc.endTime, "schedules_repeat" \`table\`
53 + FROM schedules_repeat sc
54 + INNER JOIN \`user-subject\` us
55 + ON sc.userID = us.userID
56 + AND sc.subjectID = us.subjectID
57 + AND us.status = 1
58 + INNER JOIN subjects sbj
59 + ON sc.subjectID = sbj.ID
60 + WHERE sc.ID = LAST_INSERT_ID()
61 + ORDER BY sc.startTime`;
62 + [results] = await connection.query(queryString);
63 + res.send(results[0]);
64 + } catch (e) {
65 + console.log(e);
66 + res.end();
67 + }
68 + });
69 +}
70 +route();
71 +
72 +module.exports = schedules_repeatRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const schedules_timeRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID, date) => schedules
10 + schedules_timeRouter.get("/", async (req, res) => {
11 + // console.log("/db/schedules_time");
12 + try {
13 + const queryString = `
14 + SELECT sc.label, sc.type, sc.description, sc.url, sc.detail, sbj.name, us.nickname,
15 + us.color, sc.uid, sc.startTime, sc.endTime, "schedules_time" \`table\`
16 + FROM schedules_time sc
17 + INNER JOIN \`user-subject\` us
18 + ON sc.userID = us.userID
19 + AND sc.subjectID = us.subjectID
20 + AND us.status = 1
21 + INNER JOIN subjects sbj
22 + ON sc.subjectID = sbj.ID
23 + WHERE sc.date = "${req.query.date}"
24 + AND sc.userID = ${req.query.userID}
25 + AND sc.status = 1
26 + ORDER BY sc.endTime`;
27 + const [results] = await connection.query(queryString);
28 + res.send(results);
29 + } catch (e) {
30 + console.log(e);
31 + res.end();
32 + }
33 + });
34 +
35 + // (userID, label, subjectID, type, description, url, date, startTime, endTime)
36 + schedules_timeRouter.post("/", async (req, res) => {
37 + console.log("post /db/schedules_time");
38 + try {
39 + const startTime = req.body.startTime ? `"${req.body.startTime}"` : "null";
40 + const endTime = req.body.endTime ? `"${req.body.endTime}"` : "null";
41 + let queryString = `
42 + INSERT INTO \`schedules_time\`
43 + (userID, label, subjectID, type, description, url, date, status, startTime, endTime)
44 + VALUES (${req.body.userID}, "${req.body.label}", ${req.body.subjectID}, "${req.body.type}", "${req.body.description}",
45 + "${req.body.url}", "${req.body.date}", 1, ${startTime}, ${endTime});`;
46 + await connection.query(queryString);
47 +
48 + queryString = `
49 + UPDATE schedules_time
50 + SET uid = -ID
51 + WHERE ID = LAST_INSERT_ID()`;
52 + await connection.query(queryString);
53 +
54 + queryString = `SELECT sc.label, sc.type, sc.description, sc.url, sc.detail, sbj.name, us.nickname,
55 + us.color, sc.uid, sc.startTime, sc.endTime, "schedules_time" \`table\`
56 + FROM schedules_time sc
57 + INNER JOIN \`user-subject\` us
58 + ON sc.userID = us.userID
59 + AND sc.subjectID = us.subjectID
60 + AND us.status = 1
61 + INNER JOIN subjects sbj
62 + ON sc.subjectID = sbj.ID
63 + WHERE sc.ID = LAST_INSERT_ID()
64 + ORDER BY sc.endTime`;
65 + [results] = await connection.query(queryString);
66 + res.send(results[0]);
67 + } catch (e) {
68 + console.log(e);
69 + res.end();
70 + }
71 + });
72 +}
73 +route();
74 +
75 +module.exports = schedules_timeRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const subjectsRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (ID) => null | name //unused
10 + subjectsRouter.get("/", async (req, res) => {
11 + console.log("/db/subjects");
12 + try {
13 + const queryString = `
14 + SELECT name FROM subjects sbj
15 + WHERE sbj.ID = ${req.query.ID}`;
16 + const [results] = await connection.query(queryString);
17 + res.send(results.length && results[0].name);
18 + } catch (e) {
19 + console.log(e);
20 + res.end();
21 + }
22 + });
23 +}
24 +route();
25 +
26 +module.exports = subjectsRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const userSubjectRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (userID) => [{userID, subjectID, nickname, status, color, name}, ...]
10 + userSubjectRouter.get("/", async (req, res) => {
11 + console.log("/db/user-subject");
12 + try {
13 + const queryString = `
14 + SELECT us.userID, us.subjectID, us.nickname, us.status, us.color, sub.name
15 + FROM \`user-subject\` us
16 + INNER JOIN subjects sub
17 + ON us.subjectID = sub.ID
18 + WHERE us.userID = ${req.query.userID}`;
19 + const [results] = await connection.query(queryString);
20 + res.send(results);
21 + } catch (e) {
22 + console.log(e);
23 + res.end();
24 + }
25 + });
26 +
27 + // (userID, subjectID, status)
28 + userSubjectRouter.put("/check", async (req, res) => {
29 + console.log("/db/user-subject/check");
30 + try {
31 + const queryString = `
32 + UPDATE \`user-subject\`
33 + SET status = ${req.body.status}
34 + WHERE userID = ${req.body.userID}
35 + AND subjectID = ${req.body.subjectID}`;
36 + await connection.query(queryString);
37 + res.end();
38 + } catch (e) {
39 + console.log(e);
40 + res.end();
41 + }
42 + });
43 +
44 + // (userID, subjectID, nickname, color)
45 + userSubjectRouter.put("/modify", async (req, res) => {
46 + console.log("/db/user-subject/modify");
47 + try {
48 + const queryString = `
49 + UPDATE \`user-subject\`
50 + SET color = '${req.body.color}',
51 + nickname = '${req.body.nickname}'
52 + WHERE userID = ${req.body.userID}
53 + AND subjectID = ${req.body.subjectID}`;
54 + await connection.query(queryString);
55 + res.end();
56 + } catch (e) {
57 + console.log(e);
58 + res.end();
59 + }
60 + });
61 +}
62 +route();
63 +
64 +module.exports = userSubjectRouter;
1 +const express = require("express");
2 +const mysql2 = require("mysql2/promise");
3 +const { connectOption } = require("../libs/MySQL");
4 +const usersRouter = express.Router();
5 +
6 +async function route() {
7 + const connection = await mysql2.createConnection(connectOption);
8 +
9 + // (loginID) => null | ID(str)
10 + usersRouter.get("/", async (req, res) => {
11 + console.log("/db/users");
12 + try {
13 + const queryString = `
14 + SELECT ID FROM users us
15 + WHERE us.loginID = '${req.query.loginID}'`;
16 + const [results] = await connection.query(queryString);
17 + res.send(results.length ? results[0].ID.toString() : null);
18 + } catch (e) {
19 + console.log(e);
20 + res.end();
21 + }
22 + });
23 +
24 + // (loginID, loginPW) => null | "correct"
25 + usersRouter.get("/check", async (req, res) => {
26 + console.log("/db/users/check");
27 + try {
28 + const queryString = `
29 + SELECT loginPW FROM users us
30 + WHERE us.loginID = '${req.query.loginID}'`;
31 + const [results] = await connection.query(queryString);
32 + res.send(results[0].loginPW === req.query.loginPW ? "correct" : null);
33 + } catch (e) {
34 + console.log(e);
35 + res.end();
36 + }
37 + });
38 +}
39 +route();
40 +
41 +module.exports = usersRouter;
1 const express = require("express"); 1 const express = require("express");
2 -const bodyParser = require("body-parser");
3 const fs = require("fs"); 2 const fs = require("fs");
4 const cors = require("cors"); 3 const cors = require("cors");
5 -const { getPeed, getSubjects, login } = require("./libs/E_Campus.js");
6 4
7 const loginRouter = require("./routers/Login"); 5 const loginRouter = require("./routers/Login");
8 const dbRouter = require("./routers/DB"); 6 const dbRouter = require("./routers/DB");
...@@ -15,12 +13,13 @@ app.use( ...@@ -15,12 +13,13 @@ app.use(
15 credentials: true, 13 credentials: true,
16 }) 14 })
17 ); 15 );
18 -app.use(bodyParser.urlencoded({ extended: false })); 16 +// app.use(bodyParser.urlencoded({ extended: false }));
19 -app.use(bodyParser.json()); 17 +// app.use(bodyParser.json());
18 +app.use(express.json());
19 +app.use(express.urlencoded({ extended: true }));
20 20
21 app.post("/", (req, res) => { 21 app.post("/", (req, res) => {
22 - res.send({ body: req.body }); 22 + console.log("/");
23 - console.log("listened /");
24 }); 23 });
25 24
26 app.use("/login", loginRouter); 25 app.use("/login", loginRouter);
......
1 -import { useContext, useEffect, useState } from "react"; 1 +import { useContext, useEffect, useRef, useState } from "react";
2 +import localforage from "localforage";
2 3
3 import { toYMD, toYMDStr } from "../utils/Dates"; 4 import { toYMD, toYMDStr } from "../utils/Dates";
4 -import { scheForage } from "../utils/LocalForage";
5 5
6 import { CalendarStateContext } from "../pages/Calendar"; 6 import { CalendarStateContext } from "../pages/Calendar";
7 import ScheduleItem from "./ScheduleItem"; 7 import ScheduleItem from "./ScheduleItem";
8 +import axios from "axios";
8 9
9 const GridItem = ({ targetDate }) => { 10 const GridItem = ({ targetDate }) => {
10 - const { state } = useContext(CalendarStateContext); 11 + const { state: calState, subsObj } = useContext(CalendarStateContext);
11 - const { month: calMonth } = toYMD(state.date); 12 + const { month: calMonth } = toYMD(calState.date);
12 const { month, date } = toYMD(targetDate); 13 const { month, date } = toYMD(targetDate);
13 const [schedules, setSchedules] = useState(); 14 const [schedules, setSchedules] = useState();
15 + const [state, setState] = useState({
16 + label: "",
17 + startTime: "",
18 + endTime: "",
19 + description: "",
20 + url: "",
21 + type: "assignment",
22 + subjectID: 0,
23 + });
14 24
15 useEffect(() => { 25 useEffect(() => {
16 async function loadScheduleItems() { 26 async function loadScheduleItems() {
17 - setSchedules(await scheForage.getItem(toYMDStr(targetDate))); 27 + const params = {
28 + userID: await localforage.getItem("userID"),
29 + date: toYMDStr(targetDate, "-"),
30 + day: targetDate.getDay(),
31 + };
32 + const { data: scdate } = await axios.get(
33 + "http://localhost:3001/db/schedules_date",
34 + { params }
35 + );
36 + const { data: sctime } = await axios.get(
37 + "http://localhost:3001/db/schedules_time",
38 + { params }
39 + );
40 + const { data: scrpeat } = await axios.get(
41 + "http://localhost:3001/db/schedules_repeat",
42 + { params }
43 + );
44 + const subs = await localforage.getItem("subjects");
45 + setState({ ...state, subjectID: subs[0].subjectID });
46 + setSchedules(scrpeat.concat(scdate, sctime));
18 } 47 }
19 loadScheduleItems(); 48 loadScheduleItems();
20 }, [targetDate]); 49 }, [targetDate]);
21 50
51 + const handleChangeState = (e) => {
52 + setState({
53 + ...state,
54 + [e.target.name]: e.target.value,
55 + });
56 + };
57 +
58 + const finishSchedule = async (table, uid) => {
59 + for (const i in schedules)
60 + if (schedules[i].uid === uid) {
61 + await axios.delete("http://localhost:3001/db/schedule", {
62 + data: {
63 + table,
64 + uid,
65 + userID: await localforage.getItem("userID"),
66 + },
67 + });
68 + schedules.splice(i, 1);
69 + setSchedules(schedules);
70 + }
71 + };
72 +
73 + const popupRef = useRef();
74 + const click = async (e) => {
75 + if (e.target.classList.contains("gi")) {
76 + popupRef.current.style.display = "flex";
77 + } else if (e.target.className === "gipc") {
78 + popupRef.current.style.display = "none";
79 + } else if (e.target.className === "gipa") {
80 + // (userID, label, subjectID, type, description, url, date)
81 + const table =
82 + state.startTime || state.endTime ? "schedules_time" : "schedules_date";
83 + const { data: sche } = await axios.post(
84 + "http://localhost:3001/db/" + table,
85 + {
86 + userID: await localforage.getItem("userID"),
87 + ...state,
88 + date: toYMDStr(targetDate, "-"),
89 + }
90 + );
91 + setSchedules(schedules.concat(sche));
92 + popupRef.current.style.display = "none";
93 + }
94 + };
95 +
22 return ( 96 return (
23 - <div className="GridItem" relative={month - calMonth || null}> 97 + <div
24 - <span className="date"> 98 + className="GridItem gi"
99 + relative={month - calMonth || null}
100 + onClick={click}
101 + >
102 + <span className="date gi">
25 {calMonth !== month ? month + "/" + date : date} 103 {calMonth !== month ? month + "/" + date : date}
26 </span> 104 </span>
27 {schedules && 105 {schedules &&
28 schedules.map((sche, index) => ( 106 schedules.map((sche, index) => (
29 - <ScheduleItem key={index} schedule={sche} /> 107 + <ScheduleItem key={index} schedule={sche} finish={finishSchedule} />
108 + ))}
109 + <div className="gi_popup" popup="true" ref={popupRef}>
110 + <span>일정 추가</span>
111 + <div className="gipd">
112 + <input
113 + name="label"
114 + placeholder="이름"
115 + value={state.label}
116 + onChange={handleChangeState}
117 + />
118 + <select
119 + name="subjectID"
120 + value={state.subjectID}
121 + onChange={handleChangeState}
122 + >
123 + {Object.values(subsObj).map((sub, index) => (
124 + <option key={index} value={sub.subjectID}>
125 + {sub.name}
126 + </option>
30 ))} 127 ))}
128 + </select>
129 + <select name="type" value={state.type} onChange={handleChangeState}>
130 + <option value={"assignment"}>E-Campus</option>
131 + <option value={"zoom"}>Zoom</option>
132 + </select>
133 + <input
134 + name="startTime"
135 + placeholder="시작 (HH:MM)"
136 + value={state.startTime}
137 + onChange={handleChangeState}
138 + />
139 + <input
140 + name="endTime"
141 + placeholder="종료 (HH:MM)"
142 + value={state.endTime}
143 + onChange={handleChangeState}
144 + />
145 + <textarea
146 + name="description"
147 + placeholder="설명"
148 + value={state.description}
149 + onChange={handleChangeState}
150 + />
151 + <textarea
152 + name="url"
153 + placeholder="링크"
154 + value={state.url}
155 + onChange={handleChangeState}
156 + />
157 + </div>
158 + <div className="gip_btn">
159 + <button className="gipc">취소</button>
160 + <button className="gipa">추가</button>
161 + </div>
162 + </div>
31 </div> 163 </div>
32 ); 164 );
33 }; 165 };
......
1 +import localforage from "localforage";
1 import { useContext } from "react"; 2 import { useContext } from "react";
2 import { useNavigate } from "react-router-dom"; 3 import { useNavigate } from "react-router-dom";
3 4
4 import { CalendarStateContext } from "../pages/Calendar"; 5 import { CalendarStateContext } from "../pages/Calendar";
5 import "../styles/Header.css"; 6 import "../styles/Header.css";
6 import { moveDate, toYMD } from "../utils/Dates"; 7 import { moveDate, toYMD } from "../utils/Dates";
7 -import { dataForage } from "../utils/LocalForage";
8 8
9 const Header = () => { 9 const Header = () => {
10 const { state, setState } = useContext(CalendarStateContext); 10 const { state, setState } = useContext(CalendarStateContext);
...@@ -93,7 +93,7 @@ const Header = () => { ...@@ -93,7 +93,7 @@ const Header = () => {
93 <button 93 <button
94 className="hrb_r" 94 className="hrb_r"
95 onClick={async () => { 95 onClick={async () => {
96 - await dataForage.setItem("session", ""); 96 + await localforage.setItem("session", null);
97 navigate("/"); 97 navigate("/");
98 }} 98 }}
99 > 99 >
......
1 -import { useContext } from "react"; 1 +import { useRef } from "react";
2 -
3 -import { CalendarStateContext } from "../pages/Calendar";
4 import zoomSymbol from "../assets/zoom.png"; 2 import zoomSymbol from "../assets/zoom.png";
5 import ecampusSymbol from "../assets/e-Campus.png"; 3 import ecampusSymbol from "../assets/e-Campus.png";
6 4
7 -const ScheduleItem = ({ schedule }) => { 5 +const ScheduleItem = ({ schedule, finish }) => {
8 - const { subCode, type, category, label, start, end } = schedule; 6 + const {
9 - const { subsObj } = useContext(CalendarStateContext); 7 + name: subjectName,
10 - const subject = subsObj[subCode]; 8 + nickname: subjectNickname,
11 - if (!subject) { 9 + uid: scheUID,
12 - console.log("can't find " + subCode); 10 + color: subjectColor,
13 - return; 11 + url,
14 - } 12 + type,
15 - if (!subject.selected) return; 13 + label,
14 + description = null,
15 + detail = null,
16 + startTime = null, //HHMMSS문자열
17 + endTime = null,
18 + table,
19 + } = schedule;
20 + let sTime = startTime ? startTime.substring(0, 5) : ""; //HHMM
21 + let eTime = endTime ? endTime.substring(0, 5) : "";
16 22
17 const selectSymbol = () => { 23 const selectSymbol = () => {
18 let symbol; 24 let symbol;
...@@ -20,7 +26,7 @@ const ScheduleItem = ({ schedule }) => { ...@@ -20,7 +26,7 @@ const ScheduleItem = ({ schedule }) => {
20 case "zoom": 26 case "zoom":
21 symbol = zoomSymbol; 27 symbol = zoomSymbol;
22 break; 28 break;
23 - case "ecampus": 29 + case "assignment":
24 symbol = ecampusSymbol; 30 symbol = ecampusSymbol;
25 break; 31 break;
26 default: 32 default:
...@@ -28,13 +34,53 @@ const ScheduleItem = ({ schedule }) => { ...@@ -28,13 +34,53 @@ const ScheduleItem = ({ schedule }) => {
28 return symbol; 34 return symbol;
29 }; 35 };
30 36
37 + const popupRef = useRef();
38 + const thisRef = useRef();
39 +
40 + const click = (e) => {
41 + //Item
42 + if (e.target.classList.contains("ss"))
43 + popupRef.current.style.display = "grid";
44 + else {
45 + if (e.target.className === "spc") popupRef.current.style.display = "none"; //popup close
46 + if (e.target.className === "spd") {
47 + finish(table, scheUID);
48 + thisRef.current.style.display = "none";
49 + }
50 + }
51 + };
52 +
31 return ( 53 return (
32 - <div className="ScheduleItem" style={{ borderColor: subject.color }}> 54 + <div
33 - <img className="s_symbol" src={selectSymbol()} alt="404" /> 55 + className="ScheduleItem ss"
34 - {start && <span className="s_start">{start[0] + ":" + start[1]}</span>} 56 + style={{ borderColor: "#" + subjectColor }}
35 - {end && <span className="s_end">{end[0] + ":" + end[1]}</span>} 57 + onClick={click}
36 - <span className="s_category">{category}</span> 58 + ref={thisRef}
37 - <span className="s_slabel">{label}</span> 59 + >
60 + <img className="s_symbol ss" src={selectSymbol()} alt="404" />
61 + {startTime && <span className="s_start ss">{sTime}</span>}
62 + {endTime && <span className="s_end ss">{eTime}</span>}
63 + <span className="s_slabel ss">{label}</span>
64 +
65 + <div className="s_popup" popup="true" ref={popupRef}>
66 + <div className="spl">
67 + <span>{subjectName}</span>
68 + {url ? (
69 + <a href={url} target="_blank">
70 + {label}
71 + </a>
72 + ) : (
73 + <span>{label}</span>
74 + )}
75 + {(startTime || endTime) && <span>{sTime + " ~ " + eTime}</span>}
76 + {description && <span>{description}</span>}
77 + {detail && <div dangerouslySetInnerHTML={{ __html: detail }}></div>}
78 + </div>
79 + <div className="sp_btn">
80 + <button className="spd">완료</button>
81 + <button className="spc">닫기</button>
82 + </div>
83 + </div>
38 </div> 84 </div>
39 ); 85 );
40 }; 86 };
......
...@@ -11,12 +11,7 @@ const Side = () => { ...@@ -11,12 +11,7 @@ const Side = () => {
11 11
12 for (const code in subsObj) { 12 for (const code in subsObj) {
13 sideSubjects.push( 13 sideSubjects.push(
14 - <SideSubject 14 + <SideSubject key={code} subject={subsObj[code]} dispatch={dispatch} />
15 - key={code}
16 - code={code}
17 - subject={subsObj[code]}
18 - dispatch={dispatch}
19 - />
20 ); 15 );
21 } 16 }
22 17
......
1 -const SideSubject = ({ code, subject, dispatch }) => { 1 +import { useRef, useState } from "react";
2 +
3 +const SideSubject = ({ subject, dispatch }) => {
2 const defaultColor = "#EFEFEF"; 4 const defaultColor = "#EFEFEF";
5 + const [state, setState] = useState({
6 + nickname: subject.nickname || subject.name,
7 + color: subject.color,
8 + });
9 +
10 + const handleChangeState = (e) => {
11 + setState({
12 + ...state,
13 + [e.target.name]: e.target.value,
14 + });
15 + };
3 16
4 const check = (e) => { 17 const check = (e) => {
5 - dispatch({ type: "CHECKED", code }); 18 + dispatch({ type: "CHECKED", subjectID: subject.subjectID });
6 - if (subject.selected) e.target.style["background-color"] = defaultColor; 19 + if (subject.status) e.target.style["background-color"] = defaultColor;
7 else e.target.style["background-color"] = subject.color; 20 else e.target.style["background-color"] = subject.color;
8 }; 21 };
9 22
23 + const popupRef = useRef();
24 +
25 + const labelClick = (e) => {
26 + popupRef.current.style.display = "flex";
27 + };
28 +
29 + const popupClose = (e) => {
30 + setState({
31 + nickname: subject.nickname || subject.name,
32 + color: subject.color,
33 + });
34 + popupRef.current.style.display = "none";
35 + };
36 +
37 + const popupApply = (e) => {
38 + if (state.nickname.length < 1) alert("이름을 입력해 주세요");
39 + else if (state.color.length !== 6) alert("색상은 6자리 16진수 값 입니다");
40 + else {
41 + let check = true;
42 + for (const c of state.color) if (isNaN(parseInt(c, 16))) check = false;
43 + if (!check) alert("색상은 16진수 값 입니다");
44 + else {
45 + dispatch({
46 + type: "MODIFY",
47 + subjectID: subject.subjectID,
48 + nickname: state.nickname,
49 + color: state.color,
50 + });
51 + e.target.offsetParent.style.display = "none";
52 + }
53 + }
54 + };
55 +
10 return ( 56 return (
11 <div className="SideSubject"> 57 <div className="SideSubject">
12 <div 58 <div
13 className="ssc" 59 className="ssc"
14 onClick={check} 60 onClick={check}
15 style={{ 61 style={{
16 - backgroundColor: subject.selected ? subject.color : defaultColor, 62 + backgroundColor: subject.status ? "#" + subject.color : defaultColor,
17 }} 63 }}
18 ></div> 64 ></div>
19 - 65 + <span className="ssl" onClick={labelClick}>
20 - <span>{subject.name}</span> 66 + {subject.nickname || subject.name}
67 + </span>
68 + <div className="ss_popup" popup="true" ref={popupRef}>
69 + <div className="sspd">
70 + <div className="sspd_1">
71 + <span>이름</span>
72 + <input
73 + name="nickname"
74 + value={state.nickname}
75 + onChange={handleChangeState}
76 + />
77 + </div>
78 + <div className="sspd_2">
79 + <span>색상</span>
80 + <input
81 + name="color"
82 + value={state.color}
83 + onChange={handleChangeState}
84 + />
85 + </div>
86 + </div>
87 + <div className="ssp_btns">
88 + <button onClick={popupApply}>적용 </button>
89 + <button onClick={popupClose}>취소</button>
90 + </div>
91 + </div>
21 </div> 92 </div>
22 ); 93 );
23 }; 94 };
......
1 +import React, { useContext } from "react";
2 +import GridItem from "./GridItem.js";
3 +
4 +import { CalendarStateContext } from "../pages/Calendar";
5 +import { moveDate, toSunday } from "../utils/Dates";
6 +import "../styles/Week.css";
7 +
8 +const GridHead = () => {
9 + const days = ["일", "월", "화", "수", "목", "금", "토"];
10 + const renderItems = () => {
11 + const items = [];
12 + for (let i = 0; i < 7; i++) {
13 + items.push(
14 + <div className="GridHeadItem" key={i}>
15 + {days[i]}
16 + </div>
17 + );
18 + }
19 + return items;
20 + };
21 +
22 + return <div className="GridHead">{renderItems()}</div>;
23 +};
24 +
25 +const Row = () => {
26 + const { state } = useContext(CalendarStateContext);
27 +
28 + const renderItems = () => {
29 + const items = [];
30 + const ndate = new Date(state.date);
31 + toSunday(ndate);
32 +
33 + for (let i = 0; i < 7; i++) {
34 + items.push(<GridItem key={i} targetDate={new Date(ndate)} />);
35 + moveDate(ndate, "day", 1);
36 + }
37 +
38 + return items;
39 + };
40 +
41 + return <div className="Row">{renderItems()}</div>;
42 +};
43 +
44 +const Week = () => {
45 + return (
46 + <div className="Week">
47 + <GridHead />
48 + <Row />
49 + </div>
50 + );
51 +};
52 +
53 +export default Week;
1 import React, { useEffect, useReducer, useState } from "react"; 1 import React, { useEffect, useReducer, useState } from "react";
2 import { useNavigate, Route, Routes } from "react-router-dom"; 2 import { useNavigate, Route, Routes } from "react-router-dom";
3 3
4 -import { initTempSubjects } from "../utils/Test";
5 -import { dataForage, subForage } from "../utils/LocalForage";
6 -
7 import Month from "../components/Month"; 4 import Month from "../components/Month";
8 import Header from "../components/Header"; 5 import Header from "../components/Header";
9 import Side from "../components/Side"; 6 import Side from "../components/Side";
7 +import localforage from "localforage";
8 +import axios from "axios";
9 +import Week from "../components/Week";
10 10
11 export const CalendarStateContext = React.createContext(); 11 export const CalendarStateContext = React.createContext();
12 12
13 const render = (subsObj, args) => { 13 const render = (subsObj, args) => {
14 + let sub;
14 switch (args.type) { 15 switch (args.type) {
15 case "CHECKED": 16 case "CHECKED":
16 - const sub = subsObj[args.code]; 17 + sub = subsObj[args.subjectID];
17 - sub.selected = !sub.selected; 18 + sub.status = !sub.status;
18 - subForage.setItem(args.code, sub); 19 + axios.put("http://localhost:3001/db/user-subject/check", {
19 - return { ...subsObj, [args.code]: sub }; 20 + userID: sub.userID,
21 + subjectID: args.subjectID,
22 + status: +sub.status,
23 + });
24 + return { ...subsObj, [args.subjectID]: sub };
25 + case "MODIFY":
26 + sub = subsObj[args.subjectID];
27 + sub.nickname = args.nickname;
28 + sub.color = args.color;
29 + axios.put("http://localhost:3001/db/user-subject/modify", {
30 + userID: sub.userID,
31 + subjectID: args.subjectID,
32 + nickname: sub.nickname,
33 + color: sub.color,
34 + });
35 + return { ...subsObj, [args.subjectID]: sub };
20 case "INIT": 36 case "INIT":
21 return args.subsObj; 37 return args.subsObj;
22 default: 38 default:
...@@ -36,12 +52,17 @@ const Calendar = () => { ...@@ -36,12 +52,17 @@ const Calendar = () => {
36 const navigate = useNavigate(); 52 const navigate = useNavigate();
37 useEffect(() => { 53 useEffect(() => {
38 async function onMount() { 54 async function onMount() {
39 - if (!(await dataForage.getItem("session"))) return navigate("/login"); 55 + if (!(await localforage.getItem("session"))) return navigate("/login");
40 - 56 + // get user's subjects
41 - if (!(await dataForage.getItem("Subjects"))) await initTempSubjects(); 57 + const userID = await localforage.getItem("userID");
58 + const { data: subjects } = await axios.get(
59 + "http://localhost:3001/db/user-subject",
60 + { params: { userID } }
61 + );
62 + await localforage.setItem("subjects", subjects);
42 let tsubsObj = {}; 63 let tsubsObj = {};
43 - for (const code of await dataForage.getItem("Subjects")) { 64 + for (const sub of subjects) {
44 - tsubsObj[code] = await subForage.getItem(code); 65 + tsubsObj[sub.subjectID] = sub;
45 } 66 }
46 dispatch({ type: "INIT", subsObj: tsubsObj }); 67 dispatch({ type: "INIT", subsObj: tsubsObj });
47 } 68 }
...@@ -58,7 +79,7 @@ const Calendar = () => { ...@@ -58,7 +79,7 @@ const Calendar = () => {
58 <Side /> 79 <Side />
59 <Routes> 80 <Routes>
60 <Route path="/month/*" element={<Month />} /> 81 <Route path="/month/*" element={<Month />} />
61 - <Route path="/week/*" element={<></>} /> 82 + <Route path="/week/*" element={<Week />} />
62 <Route path="/day/*" element={<></>} /> 83 <Route path="/day/*" element={<></>} />
63 </Routes> 84 </Routes>
64 </div> 85 </div>
......
...@@ -4,7 +4,6 @@ import { useNavigate } from "react-router-dom"; ...@@ -4,7 +4,6 @@ import { useNavigate } from "react-router-dom";
4 //import { login } from "../libs/E_Campus"; 4 //import { login } from "../libs/E_Campus";
5 5
6 import "../styles/Debug.css"; 6 import "../styles/Debug.css";
7 -import { initTempSubjects } from "../utils/Test";
8 7
9 const Debug = () => { 8 const Debug = () => {
10 console.log("visit Debug"); 9 console.log("visit Debug");
...@@ -26,10 +25,6 @@ const Debug = () => { ...@@ -26,10 +25,6 @@ const Debug = () => {
26 const handleSubmit = async (e) => { 25 const handleSubmit = async (e) => {
27 let result; 26 let result;
28 switch (state.type) { 27 switch (state.type) {
29 - case "initSubjects":
30 - await initTempSubjects();
31 - result = "inited";
32 - break;
33 case "login": 28 case "login":
34 //result = login(state.input1, state.input2); 29 //result = login(state.input1, state.input2);
35 break; 30 break;
...@@ -66,7 +61,6 @@ const Debug = () => { ...@@ -66,7 +61,6 @@ const Debug = () => {
66 <div> 61 <div>
67 <span>type : </span> 62 <span>type : </span>
68 <select name="type" value={state.type} onChange={handleChangeState}> 63 <select name="type" value={state.type} onChange={handleChangeState}>
69 - <option value={"initSubjects"}>initSubjects</option>
70 <option value={"login"}>login</option> 64 <option value={"login"}>login</option>
71 <option value={"server"}>server</option> 65 <option value={"server"}>server</option>
72 </select> 66 </select>
......
1 +import localforage from "localforage";
1 import { useEffect } from "react"; 2 import { useEffect } from "react";
2 import { useNavigate } from "react-router-dom"; 3 import { useNavigate } from "react-router-dom";
3 4
4 -import { dataForage } from "../utils/LocalForage";
5 -
6 const Home = () => { 5 const Home = () => {
7 console.log("visit Home"); 6 console.log("visit Home");
8 7
...@@ -10,7 +9,7 @@ const Home = () => { ...@@ -10,7 +9,7 @@ const Home = () => {
10 useEffect(() => { 9 useEffect(() => {
11 async function where() { 10 async function where() {
12 let destination; 11 let destination;
13 - if (await dataForage.getItem("session")) { 12 + if (await localforage.getItem("session")) {
14 destination = "/calendar/month"; 13 destination = "/calendar/month";
15 } else { 14 } else {
16 destination = "/login"; 15 destination = "/login";
......
1 import { useEffect, useState } from "react"; 1 import { useEffect, useState } from "react";
2 import { useNavigate } from "react-router-dom"; 2 import { useNavigate } from "react-router-dom";
3 +import localforage from "localforage";
3 4
4 -import { dataForage } from "../utils/LocalForage";
5 import "../styles/Login.css"; 5 import "../styles/Login.css";
6 import axios from "axios"; 6 import axios from "axios";
7 +import cryptoJs from "crypto-js";
7 8
8 const Login = () => { 9 const Login = () => {
9 console.log("visit Login"); 10 console.log("visit Login");
10 const [state, setState] = useState({ 11 const [state, setState] = useState({
11 id: "", 12 id: "",
12 pw: "", 13 pw: "",
14 + btn: "Login",
13 }); 15 });
14 16
15 const handleChangeState = (e) => { 17 const handleChangeState = (e) => {
...@@ -21,21 +23,57 @@ const Login = () => { ...@@ -21,21 +23,57 @@ const Login = () => {
21 23
22 const navigate = useNavigate(); 24 const navigate = useNavigate();
23 const login = async () => { 25 const login = async () => {
24 - const res = await axios.post("http://localhost:3001/login/", { 26 + setState({ ...state, btn: "Login..." });
25 - id: state.id, 27 + const { data: userDBID } = await axios.get(
26 - pw: state.pw, 28 + "http://localhost:3001/db/users",
27 - }); 29 + {
30 + params: {
31 + loginID: state.id,
32 + },
33 + }
34 + );
28 35
29 - if (res.data === "login failed") alert("ID/PW를 확인해주세요"); 36 + if (userDBID) {
37 + //pass crawling
38 + const hashpw = cryptoJs.SHA256(state.pw).toString();
39 + const { data: isCorrectPW } = await axios.get(
40 + "http://localhost:3001/db/users/check",
41 + {
42 + params: { loginID: state.id, loginPW: hashpw },
43 + }
44 + );
45 + if (isCorrectPW) await localforage.setItem("userID", Number(userDBID));
30 else { 46 else {
31 - await dataForage.setItem("session", true); 47 + setState({ ...state, btn: "Login" });
32 - navigate("/"); 48 + alert("ID/PW를 확인해주세요");
49 + return;
33 } 50 }
51 + } else {
52 + //crawling
53 + const { data: loginResult } = await axios.post(
54 + "http://localhost:3001/login/",
55 + {
56 + id: state.id,
57 + pw: state.pw,
58 + }
59 + );
60 + if (loginResult === "login failed") {
61 + setState({ ...state, btn: "Login" });
62 + alert("ID/PW를 확인해주세요");
63 + return;
64 + }
65 + // + else (성공시) localforage에 userID추가
66 + }
67 + // + localforage에 id pw 추가
68 + await localforage.setItem("id", state.id);
69 + await localforage.setItem("pw", state.pw);
70 + await localforage.setItem("session", true);
71 + navigate("/");
34 }; 72 };
35 73
36 useEffect(() => { 74 useEffect(() => {
37 async function ifAlreadyLogined() { 75 async function ifAlreadyLogined() {
38 - if (await dataForage.getItem("session")) navigate("/"); 76 + if (await localforage.getItem("session")) navigate("/");
39 } 77 }
40 ifAlreadyLogined(); 78 ifAlreadyLogined();
41 }, [navigate]); 79 }, [navigate]);
...@@ -61,7 +99,7 @@ const Login = () => { ...@@ -61,7 +99,7 @@ const Login = () => {
61 type="password" 99 type="password"
62 /> 100 />
63 </div> 101 </div>
64 - <button onClick={login}>Login</button> 102 + <button onClick={login}>{state.btn}</button>
65 </div> 103 </div>
66 ); 104 );
67 }; 105 };
......
1 +import localforage from "localforage";
1 import { Navigate, useNavigate } from "react-router-dom"; 2 import { Navigate, useNavigate } from "react-router-dom";
2 3
3 import "../styles/Settings.css"; 4 import "../styles/Settings.css";
4 -import { dataForage } from "../utils/LocalForage";
5 5
6 const Settings = () => { 6 const Settings = () => {
7 console.log("visit Settings"); 7 console.log("visit Settings");
8 8
9 - const session = dataForage.getItem("session"); 9 + const session = localforage.getItem("session");
10 10
11 const navigate = useNavigate(); 11 const navigate = useNavigate();
12 12
......
...@@ -47,12 +47,61 @@ button:disabled { ...@@ -47,12 +47,61 @@ button:disabled {
47 display: flex; 47 display: flex;
48 } 48 }
49 49
50 +.GridItem {
51 + position: relative;
52 +}
53 +
54 +.gi_popup {
55 + flex-direction: column;
56 + padding: 5px;
57 + top: 30px;
58 +}
59 +
60 +.gi_popup > span {
61 + font-size: large;
62 +}
63 +
64 +.gip_btn {
65 + display: flex;
66 + flex-direction: row-reverse;
67 +}
68 +
69 +.gip_btn > button {
70 + padding: 5px 9px 5px 9px;
71 + margin-left: 2px;
72 +}
73 +
74 +.gipd {
75 + margin: 5px 0 5px 0;
76 + display: grid;
77 + grid-template-columns: repeat(2, minmax(100px, auto));
78 + grid-template-rows: repeat(6, minmax(30px, auto));
79 + row-gap: 5px;
80 + column-gap: 5px;
81 +}
82 +
83 +.gipd > * {
84 + grid-column: 1 / span 2;
85 +}
86 +.gipd > :nth-child(4) {
87 + grid-column: 1;
88 +}
89 +.gipd > :nth-child(5) {
90 + grid-column: 2;
91 +}
92 +.gipd > :nth-child(6),
93 +.gipd > :nth-child(7) {
94 + resize: none;
95 + height: 60px;
96 +}
97 +
50 .ScheduleItem { 98 .ScheduleItem {
51 - height: 23px;
52 display: flex; 99 display: flex;
53 margin: 1px 0 1px 0; 100 margin: 1px 0 1px 0;
54 border: solid 3px bisque; 101 border: solid 3px bisque;
55 border-radius: 3px; 102 border-radius: 3px;
103 + cursor: pointer;
104 + position: relative;
56 } 105 }
57 106
58 .ScheduleItem > span { 107 .ScheduleItem > span {
...@@ -63,7 +112,7 @@ button:disabled { ...@@ -63,7 +112,7 @@ button:disabled {
63 } 112 }
64 113
65 .s_start { 114 .s_start {
66 - color: deepskyblue; 115 + color: blue;
67 /* text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white; */ 116 /* text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white; */
68 } 117 }
69 118
...@@ -78,3 +127,61 @@ button:disabled { ...@@ -78,3 +127,61 @@ button:disabled {
78 border-radius: 3px; 127 border-radius: 3px;
79 padding: 0; 128 padding: 0;
80 } 129 }
130 +
131 +[popup] {
132 + position: absolute;
133 + z-index: 1000;
134 + background: rgb(250, 250, 250);
135 + box-shadow: 0px 0px 5px gray;
136 + border: solid thin grey;
137 + border-radius: 5px;
138 + display: none;
139 + cursor: auto;
140 +}
141 +
142 +.s_popup {
143 + top: calc(100% + 5px);
144 + left: 20px;
145 + padding: 10px;
146 + width: 300px;
147 +}
148 +
149 +.spl {
150 + display: flex;
151 + flex-direction: column;
152 + align-items: flex-start;
153 +}
154 +
155 +.spl > * {
156 + margin-bottom: 3px;
157 +}
158 +
159 +.spl > :nth-child(1) {
160 + margin-bottom: 5px;
161 + font-size: large;
162 +}
163 +
164 +.spl > :nth-child(2) {
165 + font-size: medium;
166 +}
167 +
168 +.spl > div {
169 + margin-top: 5px;
170 + font-size: small;
171 + background-color: white;
172 + border: solid 1px rgb(235, 235, 235);
173 + width: calc(100% - 2px);
174 +}
175 +
176 +.sp_btn {
177 + display: flex;
178 + margin-left: calc(100% - 100px);
179 +}
180 +
181 +.sp_btn > button {
182 + margin-left: 2px;
183 + margin-top: 2px;
184 + width: 50px;
185 + height: 35px;
186 + cursor: pointer;
187 +}
......
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
12 12
13 .GridItem { 13 .GridItem {
14 border: solid thin lightgray; 14 border: solid thin lightgray;
15 - height: 150px; 15 + display: flex;
16 + flex-direction: column;
17 + /* height: 150px; */
16 padding: 5px; 18 padding: 5px;
17 } 19 }
18 20
...@@ -34,7 +36,7 @@ ...@@ -34,7 +36,7 @@
34 .Grid { 36 .Grid {
35 display: grid; 37 display: grid;
36 grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; 38 grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
37 - overflow-y: auto; 39 + overflow-y: scroll;
38 height: calc(100vh - 115px); 40 height: calc(100vh - 115px);
39 border: solid thin gray; 41 border: solid thin gray;
40 } 42 }
......
1 aside { 1 aside {
2 width: 220px; 2 width: 220px;
3 + flex-shrink: 0;
3 margin-right: 8px; 4 margin-right: 8px;
4 padding-top: 50px; 5 padding-top: 50px;
5 } 6 }
6 7
7 .SideSubject { 8 .SideSubject {
8 - height: 25px;
9 padding: 5px 0px 5px 0px; 9 padding: 5px 0px 5px 0px;
10 display: flex; 10 display: flex;
11 + position: relative;
11 } 12 }
12 13
13 .ssc { 14 .ssc {
...@@ -20,5 +21,48 @@ aside { ...@@ -20,5 +21,48 @@ aside {
20 } 21 }
21 22
22 .SideSubject > span { 23 .SideSubject > span {
24 + cursor: pointer;
23 line-height: 25px; 25 line-height: 25px;
24 } 26 }
27 +
28 +.ss_popup {
29 + flex-direction: column;
30 + align-items: flex-end;
31 + top: 30px;
32 + left: 30px;
33 + /* height: 150px;
34 + width: 250px; */
35 + /* transform: translate(-50%, -50%); */
36 + padding: 5px;
37 +}
38 +
39 +.sspd {
40 + display: flex;
41 + flex-direction: column;
42 +}
43 +
44 +.sspd > div {
45 + display: flex;
46 + margin-bottom: 3px;
47 +}
48 +
49 +.sspd > div > span {
50 + text-align: center;
51 + line-height: 30px;
52 + margin-right: 5px;
53 + width: 35px;
54 +}
55 +
56 +.sspd > div > input {
57 + flex-grow: 1;
58 + height: 24px;
59 +}
60 +
61 +.ssp_btns {
62 + margin-top: 2px;
63 +}
64 +
65 +.ssp_btns > button {
66 + padding: 5px 9px 5px 9px;
67 + margin-left: 2px;
68 +}
......
1 +.Week {
2 + display: flex;
3 + flex-direction: column;
4 + flex-grow: 1;
5 +}
6 +
7 +.GridItem,
8 +.GridHeadItem {
9 + flex-basis: 100px;
10 + flex-grow: 1;
11 +}
12 +
13 +.GridItem {
14 + border: solid thin lightgray;
15 + display: flex;
16 + flex-direction: column;
17 + /* height: 150px; */
18 + padding: 5px;
19 +}
20 +
21 +.GridHeadItem {
22 + height: 20px;
23 + text-align: center;
24 + line-height: 20px;
25 + padding: 10px 5px 10px 5px;
26 +}
27 +
28 +.GridHead {
29 + display: flex;
30 +}
31 +
32 +.GridHead {
33 + margin-right: 16.8px;
34 +}
35 +
36 +.Row {
37 + display: flex;
38 + overflow-y: scroll;
39 + height: calc(100vh - 115px);
40 + border: solid thin gray;
41 +}
42 +
43 +.GridItem[relative] > span {
44 + color: gray;
45 +}
...@@ -6,12 +6,12 @@ function toYMD(dateObj) { ...@@ -6,12 +6,12 @@ function toYMD(dateObj) {
6 return { year, month, date }; 6 return { year, month, date };
7 } 7 }
8 8
9 -function toYMDStr(dateObj) { 9 +function toYMDStr(dateObj, joint) {
10 return [ 10 return [
11 dateObj.getFullYear(), 11 dateObj.getFullYear(),
12 dateObj.getMonth() + 1, 12 dateObj.getMonth() + 1,
13 dateObj.getDate(), 13 dateObj.getDate(),
14 - ].join("/"); 14 + ].join(joint);
15 } 15 }
16 16
17 function toSunday(dateObj) { 17 function toSunday(dateObj) {
......
1 -import { subForage, scheForage, dataForage } from "./LocalForage";
2 -import { toYMDStr } from "./Dates";
3 -
4 -async function initTempSubjects() {
5 - const tempsch = [
6 - {
7 - subCode: "1",
8 - type: "zoom",
9 - category: "과목A",
10 - label: "",
11 - start: [9, 30],
12 - },
13 - {
14 - subCode: "2",
15 - type: "ecampus",
16 - category: "과목B",
17 - label: "과제",
18 - end: [23, 59],
19 - },
20 - ];
21 -
22 - await scheForage.setItem(toYMDStr(new Date("2022-5-20")), tempsch);
23 - await scheForage.setItem(toYMDStr(new Date("2022-5-27")), tempsch);
24 - await scheForage.setItem(toYMDStr(new Date("2022-6-3")), tempsch);
25 -
26 - let tcolors = [
27 - "red",
28 - "green",
29 - "blue",
30 - "orange",
31 - "gold",
32 - "aqua",
33 - "chartreuse",
34 - ];
35 - const subCodeLst = ["1", "2"];
36 - const subObj = {
37 - 1: {
38 - name: "과목A",
39 - color: tcolors[0],
40 - selected: true,
41 - },
42 - 2: {
43 - name: "과목B",
44 - color: tcolors[1],
45 - selected: true,
46 - },
47 - };
48 -
49 - await subForage.setItem("1", subObj["1"]);
50 - await subForage.setItem("2", subObj["2"]);
51 -
52 - for (let i = 2; i < 7; i++) {
53 - let code = (i + 1).toString();
54 - let tsub = {
55 - name: "과목" + code,
56 - color: tcolors[i],
57 - selected: true,
58 - };
59 - subCodeLst.push(code);
60 - await subForage.setItem(code, tsub);
61 - }
62 - dataForage.setItem("Subjects", subCodeLst);
63 -}
64 -
65 -export { initTempSubjects };