diff --git a/index.html b/index.html index e0d1c84..d49d5a9 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,13 @@ - + - Vite + React + TS + Router App
diff --git a/package-lock.json b/package-lock.json index 5f9e78b..a3e2b35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,16 +8,26 @@ "name": "quickstart", "version": "0.0.0", "dependencies": { + "@types/youtube-player": "^5.5.7", + "axios": "^1.3.2", "bootstrap": "^5.2.3", + "date-and-time": "^2.4.2", + "immer": "^9.0.18", + "p-min-delay": "^4.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router": "^6.8.1", + "react-router-dom": "^6.8.1", + "react-spinners": "^0.13.8", + "react-youtube": "^10.1.0" }, "devDependencies": { "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@vitejs/plugin-react": "^3.0.0", "typescript": "^4.9.3", - "vite": "^4.0.0" + "vite": "^4.0.0", + "vite-plugin-webpackchunkname": "^0.2.4" } }, "node_modules/@ampproject/remapping": { @@ -784,6 +794,42 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@remix-run/router": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", + "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-alias": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz", + "integrity": "sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==", + "dev": true, + "dependencies": { + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -816,6 +862,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "node_modules/@types/youtube-player": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@types/youtube-player/-/youtube-player-5.5.7.tgz", + "integrity": "sha512-W8F4eoTIvzXeNrT3JroQPimZLXnlJA8smYygHZUKFPVoYwgs/OhJkA1VBhL3iSs57OQkuINqHlY4SmMT5wtnJg==" + }, "node_modules/@vitejs/plugin-react": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.0.1.tgz", @@ -847,6 +898,21 @@ "node": ">=4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/bootstrap": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", @@ -938,6 +1004,17 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -950,6 +1027,11 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", "dev": true }, + "node_modules/date-and-time": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.4.2.tgz", + "integrity": "sha512-h+GwuCLcrDblUt2pXdVuKJJenwNMNtEw1H/eWqBdrkOziTTPzgAkzyCLXZ+QXgtOEheO59cZa8DcObYwE6NLDA==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -967,12 +1049,26 @@ } } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "node_modules/es-module-lexer": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", + "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "dev": true + }, "node_modules/esbuild": { "version": "0.16.15", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.15.tgz", @@ -1028,6 +1124,49 @@ "node": ">=0.8.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -1087,6 +1226,15 @@ "node": ">=4" } }, + "node_modules/immer": { + "version": "9.0.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", + "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -1128,6 +1276,11 @@ "node": ">=6" } }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1160,6 +1313,25 @@ "node": ">=12" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1184,6 +1356,28 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-min-delay": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/p-min-delay/-/p-min-delay-4.0.2.tgz", + "integrity": "sha512-7hJcTq/MGF5pNHbQ2akrpPy1N43YYlB4RPECDSbPRn4xP/dsgP0I6ls7NvSUQ5k88o+CyATMOrQiZ/PK4aQR9w==", + "dependencies": { + "yoctodelay": "^1.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -1196,6 +1390,18 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -1220,6 +1426,21 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -1243,6 +1464,11 @@ "react": "^18.2.0" } }, + "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/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -1252,6 +1478,61 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", + "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "dependencies": { + "@remix-run/router": "1.3.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", + "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "dependencies": { + "@remix-run/router": "1.3.2", + "react-router": "6.8.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-youtube": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz", + "integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==", + "dependencies": { + "fast-deep-equal": "3.1.3", + "prop-types": "15.8.1", + "youtube-player": "5.5.2" + }, + "engines": { + "node": ">= 14.x" + }, + "peerDependencies": { + "react": ">=0.14.1" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -1270,16 +1551,16 @@ } }, "node_modules/rollup": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.9.1.tgz", - "integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" + "node": ">=10.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" @@ -1302,6 +1583,20 @@ "semver": "bin/semver.js" } }, + "node_modules/sister": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", + "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -1311,6 +1606,13 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1432,11 +1734,87 @@ } } }, + "node_modules/vite-plugin-webpackchunkname": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/vite-plugin-webpackchunkname/-/vite-plugin-webpackchunkname-0.2.4.tgz", + "integrity": "sha512-aVvS+cR1PjlDG4vsyks9Uzh99W+qBdKmVa+Lb8M7VHGXf57BFkvl0y1JJxb3sGm6ilx8wqGippgKEGbUORvJiA==", + "dev": true, + "dependencies": { + "@rollup/plugin-alias": "^3.1.9", + "@rollup/pluginutils": "^4.2.0", + "es-module-lexer": "^0.10.0", + "magic-string": "^0.26.1" + }, + "peerDependencies": { + "@rollup/plugin-alias": "*", + "rollup": "^2.67.2", + "vite": "*" + } + }, + "node_modules/vite-plugin-webpackchunkname/node_modules/magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.14.0.tgz", + "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true + }, + "node_modules/yoctodelay": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yoctodelay/-/yoctodelay-1.2.0.tgz", + "integrity": "sha512-12y/P9MSig9/5BEhBgylss+fkHiCRZCvYR81eH35NW9uw801cvJt31EAV+WOLcwZRZbLiIQl/hxcdXXXFmGvXg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/youtube-player": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", + "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", + "dependencies": { + "debug": "^2.6.6", + "load-script": "^1.0.0", + "sister": "^3.0.0" + } + }, + "node_modules/youtube-player/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/youtube-player/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } }, "dependencies": { @@ -1901,6 +2279,30 @@ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", "peer": true }, + "@remix-run/router": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", + "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==" + }, + "@rollup/plugin-alias": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz", + "integrity": "sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==", + "dev": true, + "requires": { + "slash": "^3.0.0" + } + }, + "@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "requires": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + } + }, "@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -1933,6 +2335,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "@types/youtube-player": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@types/youtube-player/-/youtube-player-5.5.7.tgz", + "integrity": "sha512-W8F4eoTIvzXeNrT3JroQPimZLXnlJA8smYygHZUKFPVoYwgs/OhJkA1VBhL3iSs57OQkuINqHlY4SmMT5wtnJg==" + }, "@vitejs/plugin-react": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.0.1.tgz", @@ -1955,6 +2362,21 @@ "color-convert": "^1.9.0" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "bootstrap": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", @@ -2005,6 +2427,14 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -2017,6 +2447,11 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", "dev": true }, + "date-and-time": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.4.2.tgz", + "integrity": "sha512-h+GwuCLcrDblUt2pXdVuKJJenwNMNtEw1H/eWqBdrkOziTTPzgAkzyCLXZ+QXgtOEheO59cZa8DcObYwE6NLDA==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2026,12 +2461,23 @@ "ms": "2.1.2" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "es-module-lexer": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", + "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "dev": true + }, "esbuild": { "version": "0.16.15", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.15.tgz", @@ -2074,6 +2520,32 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -2114,6 +2586,11 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "immer": { + "version": "9.0.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", + "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==" + }, "is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -2140,6 +2617,11 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2166,6 +2648,19 @@ "@jridgewell/sourcemap-codec": "^1.4.13" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2184,6 +2679,19 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "p-min-delay": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/p-min-delay/-/p-min-delay-4.0.2.tgz", + "integrity": "sha512-7hJcTq/MGF5pNHbQ2akrpPy1N43YYlB4RPECDSbPRn4xP/dsgP0I6ls7NvSUQ5k88o+CyATMOrQiZ/PK4aQR9w==", + "requires": { + "yoctodelay": "^1.2.0" + } + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2196,6 +2704,12 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -2207,6 +2721,21 @@ "source-map-js": "^1.0.2" } }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -2224,12 +2753,50 @@ "scheduler": "^0.23.0" } }, + "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==" + }, "react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", "dev": true }, + "react-router": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", + "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "requires": { + "@remix-run/router": "1.3.2" + } + }, + "react-router-dom": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", + "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "requires": { + "@remix-run/router": "1.3.2", + "react-router": "6.8.1" + } + }, + "react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "requires": {} + }, + "react-youtube": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz", + "integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==", + "requires": { + "fast-deep-equal": "3.1.3", + "prop-types": "15.8.1", + "youtube-player": "5.5.2" + } + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -2242,10 +2809,11 @@ } }, "rollup": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.9.1.tgz", - "integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, + "peer": true, "requires": { "fsevents": "~2.3.2" } @@ -2264,12 +2832,29 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "sister": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", + "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2318,6 +2903,40 @@ "postcss": "^8.4.20", "resolve": "^1.22.1", "rollup": "^3.7.0" + }, + "dependencies": { + "rollup": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.14.0.tgz", + "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + } + } + }, + "vite-plugin-webpackchunkname": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/vite-plugin-webpackchunkname/-/vite-plugin-webpackchunkname-0.2.4.tgz", + "integrity": "sha512-aVvS+cR1PjlDG4vsyks9Uzh99W+qBdKmVa+Lb8M7VHGXf57BFkvl0y1JJxb3sGm6ilx8wqGippgKEGbUORvJiA==", + "dev": true, + "requires": { + "@rollup/plugin-alias": "^3.1.9", + "@rollup/pluginutils": "^4.2.0", + "es-module-lexer": "^0.10.0", + "magic-string": "^0.26.1" + }, + "dependencies": { + "magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + } } }, "yallist": { @@ -2325,6 +2944,36 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true + }, + "yoctodelay": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yoctodelay/-/yoctodelay-1.2.0.tgz", + "integrity": "sha512-12y/P9MSig9/5BEhBgylss+fkHiCRZCvYR81eH35NW9uw801cvJt31EAV+WOLcwZRZbLiIQl/hxcdXXXFmGvXg==" + }, + "youtube-player": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", + "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", + "requires": { + "debug": "^2.6.6", + "load-script": "^1.0.0", + "sister": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } } } } diff --git a/package.json b/package.json index 83154e0..b001673 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,25 @@ "preview": "vite preview" }, "dependencies": { + "@types/youtube-player": "^5.5.7", + "axios": "^1.3.2", "bootstrap": "^5.2.3", + "date-and-time": "^2.4.2", + "immer": "^9.0.18", + "p-min-delay": "^4.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router": "^6.8.1", + "react-router-dom": "^6.8.1", + "react-spinners": "^0.13.8", + "react-youtube": "^10.1.0" }, "devDependencies": { "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@vitejs/plugin-react": "^3.0.0", "typescript": "^4.9.3", - "vite": "^4.0.0" + "vite": "^4.0.0", + "vite-plugin-webpackchunkname": "^0.2.4" } } diff --git a/src/App.css b/src/App.css index 2c5e2ef..b9d355d 100644 --- a/src/App.css +++ b/src/App.css @@ -9,6 +9,7 @@ height: 6em; padding: 1.5em; will-change: filter; + transition: filter 300ms; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); diff --git a/src/App.tsx b/src/App.tsx index cd20136..1d3f1eb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,28 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import './App.css' +import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; +import Layout from "./components/Layout"; -function App() { - const [count, setCount] = useState(0) +import Home from "./pages/Home"; +import About from "./pages/About"; +import TodoList from "./pages/TodoList"; +import AddTodo from "./pages/AddTodo"; +import EditTodo from "./pages/EditTodo"; +import NotFound from "./pages/NotFound"; +const App = () => { return ( -
-
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

-
- ) -} + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); +}; -export default App +export default App; diff --git a/src/AppContainer.tsx b/src/AppContainer.tsx new file mode 100644 index 0000000..10a7fa3 --- /dev/null +++ b/src/AppContainer.tsx @@ -0,0 +1,157 @@ +import { useEffect, useState } from "react"; +import App from "./App"; +import produce from "immer"; +import axios from "axios"; + +export type TodoItemType = { + id: number; + todo: string; + desc: string; + done: boolean; +}; +export type StatesType = { todoList: Array; isLoading: boolean }; +export type CallbacksType = { + fetchTodoList: () => void; + addTodo: (todo: string, desc: string, callback: () => void) => void; + deleteTodo: (id: number) => void; + toggleDone: (id: number) => void; + updateTodo: ( + id: number, + todo: string, + desc: string, + done: boolean, + callback: () => void + ) => void; +}; + +const USER = "hyeseong"; +const BASEURI = "/api/todolist_long/" + USER; + +const AppContainer = () => { + let [todoList, setTodoList] = useState>([]); + let [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + fetchTodoList(); + }, []); + + const fetchTodoList = async () => { + setTodoList([]); + setIsLoading(true); + try { + const response = await axios.get(BASEURI); + setTodoList(response.data); + } catch (e) { + if (e instanceof Error) alert("조회 실패 :" + e.message); + else alert("조회 실패 :" + e); + } + setIsLoading(false); + }; + + const addTodo = async (todo: string, desc: string, callback: () => void) => { + setIsLoading(true); + try { + const response = await axios.post(BASEURI, { todo, desc }); + if (response.data.status === "success") { + let newTodoList = produce(todoList, (draft) => { + draft.push({ ...response.data.item, done: false }); + }); + setTodoList(newTodoList); + callback(); + } else { + alert("할일 추가실패:" + response.data.message); + } + } catch (e) { + if (e instanceof Error) alert("할일 추가실패:" + e.message); + else alert("할일 추가실패:" + e); + } + setIsLoading(false); + }; + + const deleteTodo = async (id: number) => { + setIsLoading(true); + try { + const response = await axios.delete(`${BASEURI}/${id}`); + if (response.data.status === "success") { + let index = todoList.findIndex((todo) => todo.id === id); + let newTodoList = produce(todoList, (draft) => { + draft.splice(index, 1); + }); + setTodoList(newTodoList); + } else { + alert("할일 삭제 실패:" + response.data.message); + } + } catch (e) { + if (e instanceof Error) alert("할일 삭제 실패:" + e.message); + else alert("할일 삭제 실패:" + e); + } + setIsLoading(false); + }; + + const toggleDone = async (id: number) => { + setIsLoading(true); + try { + let todoItem = todoList.find((todo) => todo.id === id); + const response = await axios.put(`${BASEURI}/${id}`, { + ...todoItem, + done: !todoItem?.done, + }); + if (response.data.status === "success") { + let index = todoList.findIndex((todo) => todo.id === id); + let newTodoList = produce(todoList, (draft) => { + draft[index].done = !draft[index].done; + }); + setTodoList(newTodoList); + } else { + alert("완료 토글 실패 : " + response.data.message); + } + } catch (e) { + if (e instanceof Error) alert("완료 토글 실패:" + e.message); + else alert("완료 토글 실패:" + e); + } + setIsLoading(false); + }; + + const updateTodo = async ( + id: number, + todo: string, + desc: string, + done: boolean, + callback: () => void + ) => { + setIsLoading(true); + try { + const response = await axios.put(`${BASEURI}/${id}`, { + todo, + desc, + done, + }); + if (response.data.status === "success") { + let index = todoList.findIndex((todo) => todo.id === id); + let newTodoList = produce(todoList, (draft) => { + draft[index] = { ...draft[index], todo, desc, done }; + }); + setTodoList(newTodoList); + callback(); + } else { + alert("할일 수정 실패 : " + response.data.message); + } + } catch (e) { + if (e instanceof Error) alert("할일 수정 실패 :" + e.message); + else alert("할일 수정 실패 :" + e); + } + setIsLoading(false); + }; + + const callbacks: CallbacksType = { + fetchTodoList, + addTodo, + deleteTodo, + updateTodo, + toggleDone, + }; + const states: StatesType = { todoList, isLoading }; + return ; +}; + +export default AppContainer; diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..afb0753 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,35 @@ +import { useState } from "react"; +import { Link } from "react-router-dom"; + +const Header = () => { + let [isNavShow, setIsNavShow] = useState(false); + return ( + + ); +}; + +export default Header; diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx new file mode 100644 index 0000000..e2a7cc5 --- /dev/null +++ b/src/components/Layout.tsx @@ -0,0 +1,13 @@ +import { Outlet } from "react-router"; +import Header from "./Header"; + +const Layout = () => { + return ( +
+
+ +
+ ); +}; + +export default Layout; diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx new file mode 100644 index 0000000..3d53980 --- /dev/null +++ b/src/components/Loading.tsx @@ -0,0 +1,16 @@ +import { ScaleLoader } from "react-spinners"; + +const Loading = () => { + return ( +
+
+
+

처리중

+ +
+
+
+ ); +}; + +export default Loading; diff --git a/src/favicon.svg b/src/favicon.svg new file mode 100644 index 0000000..de4aedd --- /dev/null +++ b/src/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/index.css b/src/index.css index 60b93b9..d55feb8 100644 --- a/src/index.css +++ b/src/index.css @@ -1,75 +1,23 @@ -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + padding: 0; + font-family: sans-serif; } - -h1 { - font-size: 3.2em; - line-height: 1.1; +.title { + text-align: center; + font-weight: bold; + font-size: 20pt; } - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; +.todo-done { + text-decoration: line-through; +} +.container { + padding: 10px 10px 10px 10px; } - -button:hover { - border-color: #646cff; +.panel-borderless { + border: 0; + box-shadow: none; } - -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; +.pointer { + cursor: pointer; } - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - - a:hover { - color: #747bff; - } - - button { - background-color: #f9f9f9; - } -} \ No newline at end of file diff --git a/src/logo.svg b/src/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main.tsx b/src/main.tsx index a48af53..97d2058 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,16 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import 'bootstrap/dist/css/bootstrap.css' -import App from './App' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import "bootstrap/dist/css/bootstrap.css"; +import "./index.css"; +import App from "./App"; -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - -) +import AppStore from "./redux/AppStore"; +import { Provider } from "react-redux"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + +); diff --git a/src/pages/About.tsx b/src/pages/About.tsx new file mode 100644 index 0000000..62d75f8 --- /dev/null +++ b/src/pages/About.tsx @@ -0,0 +1,8 @@ +const About = () => { + return ( +
+

About

+
+ ); +}; +export default About; diff --git a/src/pages/AddTodo.tsx b/src/pages/AddTodo.tsx new file mode 100644 index 0000000..b4557f7 --- /dev/null +++ b/src/pages/AddTodo.tsx @@ -0,0 +1,82 @@ +import { useState } from "react"; +import { useNavigate } from "react-router"; +import TodoActionCreator from "../redux/TodoActionCreator"; +import { connect } from "react-redux"; +import { AnyAction, Dispatch } from "redux"; + +type PropsType = { addTodo: (todo: string, desc: string) => void }; + +const AddTodo = ({ addTodo }: PropsType) => { + const navigate = useNavigate(); + + let [todo, setTodo] = useState(""); + let [desc, setDesc] = useState(""); + + const addTodoHandler = () => { + if (todo.trim() === "" || desc.trim() === "") { + alert("반드시 할일, 설명을 입력해야 합니다."); + return; + } + addTodo(todo, desc); + navigate("/todos"); + }; + + return ( + <> +
+
+

할일 추가

+
+
+
+
+
+ + setTodo(e.target.value)} + /> +
+
+ + +
+
+ + +
+
+
+ + ); +}; + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + addTodo: (todo: string, desc: string) => + dispatch(TodoActionCreator.addTodo({ todo, desc })), +}); + +const AddTodoContainer = connect(null, mapDispatchToProps)(AddTodo); + +export default AddTodoContainer; diff --git a/src/pages/AddTodoList.tsx b/src/pages/AddTodoList.tsx new file mode 100644 index 0000000..08e7638 --- /dev/null +++ b/src/pages/AddTodoList.tsx @@ -0,0 +1,74 @@ +import { useState } from "react"; +import { useNavigate } from "react-router"; +import { CallbacksType } from "../AppContainer"; + +type PropsType = { callbacks: CallbacksType }; + +const AddTodoList = ({ callbacks }: PropsType) => { + const navigate = useNavigate(); + + let [todo, setTodo] = useState(""); + let [desc, setDesc] = useState(""); + + const addTodoHandler = () => { + if (todo.trim() === "" || desc.trim() === "") { + alert("반드시 할일, 설명을 입력해야 합니다."); + return; + } + callbacks.addTodo(todo, desc, () => { + navigate("/todos"); + }); + }; + + return ( + <> +
+
+

할일 추가

+
+
+
+
+
+ + setTodo(e.target.value)} + /> +
+
+ + +
+
+ + +
+
+
+ + ); +}; + +export default AddTodoList; diff --git a/src/pages/EditTodo.tsx b/src/pages/EditTodo.tsx new file mode 100644 index 0000000..968a222 --- /dev/null +++ b/src/pages/EditTodo.tsx @@ -0,0 +1,112 @@ +import { useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import TodoActionCreator from "../redux/TodoActionCreator"; +import { connect } from "react-redux"; +import { AnyAction, Dispatch } from "redux"; +import { TodoItemType, TodoStatesType } from "../redux/TodoReducer"; + +type PropsType = { + updateTodo: (id: number, todo: string, desc: string, done: boolean) => void; + todoList: Array; +}; + +type TodoParam = { + id?: string; +}; + +const EditTodo = ({ todoList, updateTodo }: PropsType) => { + const navigate = useNavigate(); + let { id } = useParams(); + let todoItem = todoList.find((item) => item.id === parseInt(id ? id : "0")); + if (!todoItem) { + navigate("/todos"); + return <>; + } + const [todoOne, setTodoOne] = useState({ ...todoItem }); + + const updateTodoHandler = () => { + if (todoOne.todo.trim() === "" || todoOne.desc.trim() === "") { + alert("반드시 할일, 설명을 입력해야 합니다."); + return; + } + let { id, todo, desc, done } = todoOne; + updateTodo(id, todo, desc, done); + navigate("/todos"); + }; + + return ( + <> +
+
+

할일 수정

+
+
+
+
+
+ + setTodoOne({ ...todoOne, todo: e.target.value })} + /> +
+
+ + +
+
+ {" "} + + setTodoOne({ ...todoOne, done: e.target.checked }) + } + /> +
+
+ + +
+
+
+ + ); +}; + +const mapStateToProps = (state: TodoStatesType) => ({ + todoList: state.todoList, +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + updateTodo: (id: number, todo: string, desc: string, done: boolean) => + dispatch(TodoActionCreator.updateTodo({ id, todo, desc, done })), +}); + +const EditTodoContainer = connect( + mapStateToProps, + mapDispatchToProps +)(EditTodo); + +export default EditTodoContainer; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..ad4f25f --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,8 @@ +const Home = () => { + return ( +
+

Home

+
+ ); +}; +export default Home; diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx new file mode 100644 index 0000000..e67a5bb --- /dev/null +++ b/src/pages/NotFound.tsx @@ -0,0 +1,12 @@ +import { useLocation } from "react-router"; + +const NotFound = () => { + const location = useLocation(); + return ( +
+

존재하지 않는 경로

+

요청 경로 : {location.pathname}

+
+ ); +}; +export default NotFound; diff --git a/src/pages/TodoItem.tsx b/src/pages/TodoItem.tsx new file mode 100644 index 0000000..6306461 --- /dev/null +++ b/src/pages/TodoItem.tsx @@ -0,0 +1,39 @@ +import { useNavigate } from "react-router-dom"; +import { TodoItemType } from "../redux/TodoReducer"; + +type PropsType = { + todoItem: TodoItemType; + deleteTodo: (id: number) => void; + toggleDone: (id: number) => void; +}; + +const TodoItem = ({ todoItem, deleteTodo, toggleDone }: PropsType) => { + const navigate = useNavigate(); + let itemClassName = "list-group-item"; + if (todoItem.done) itemClassName += " list-group-item-success"; + return ( +
  • + toggleDone(todoItem.id)} + > + {todoItem.todo} + {todoItem.done ? "(완료)" : ""} + + navigate("/todos/edit/" + todoItem.id)} + > + 편집 + + deleteTodo(todoItem.id)} + > + 삭제 + +
  • + ); +}; + +export default TodoItem; diff --git a/src/pages/TodoList.tsx b/src/pages/TodoList.tsx new file mode 100644 index 0000000..b15a834 --- /dev/null +++ b/src/pages/TodoList.tsx @@ -0,0 +1,52 @@ +import { Link } from "react-router-dom"; +import TodoItem from "./TodoItem"; +import TodoActionCreator from "../redux/TodoActionCreator"; +import { AnyAction, Dispatch } from "redux"; +import { connect } from "react-redux"; +import { TodoStatesType, TodoItemType } from "../redux/TodoReducer"; + +type PropsType = { + todoList: Array; + deleteTodo: (id: number) => void; + toggleDone: (id: number) => void; +}; + +const TodoList = ({ todoList, deleteTodo, toggleDone }: PropsType) => { + let todoItems = todoList.map((item) => { + return ( + + ); + }); + return ( + <> +
    +
    + + 할일 추가 + +
    +
    +
    +
    +
      {todoItems}
    +
    +
    + + ); +}; + +const mapStateToProps = (state: TodoStatesType) => ({ + todoList: state.todoList, +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + deleteTodo: (id: number) => dispatch(TodoActionCreator.deleteTodo({ id })), + toggleDone: (id: number) => dispatch(TodoActionCreator.toggleDone({ id })), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TodoList); diff --git a/src/redux/AppStore.ts b/src/redux/AppStore.ts new file mode 100644 index 0000000..e0d09ef --- /dev/null +++ b/src/redux/AppStore.ts @@ -0,0 +1,5 @@ +import { configureStore } from "@reduxjs/toolkit"; +import TodoReducer from "./TodoReducer"; + +const Appstore = configureStore({ reducer: TodoReducer }); +export default Appstore; diff --git a/src/redux/TodoActionCreator.ts b/src/redux/TodoActionCreator.ts new file mode 100644 index 0000000..9febfc6 --- /dev/null +++ b/src/redux/TodoActionCreator.ts @@ -0,0 +1,15 @@ +import { createAction } from "@reduxjs/toolkit"; + +const TodoActionCreator = { + addTodo: createAction<{ todo: string; desc: string }>("addTodo"), + deleteTodo: createAction<{ id: number }>("deleteTodo"), + toggleDone: createAction<{ id: number }>("toggleDone"), + updateTodo: createAction<{ + id: number; + todo: string; + desc: string; + done: boolean; + }>("updateTodo"), +}; + +export default TodoActionCreator; diff --git a/src/redux/TodoReducer.ts b/src/redux/TodoReducer.ts new file mode 100644 index 0000000..afa283f --- /dev/null +++ b/src/redux/TodoReducer.ts @@ -0,0 +1,59 @@ +import { createReducer } from "@reduxjs/toolkit"; +import TodoActionCreator from "./TodoActionCreator"; + +export type TodoItemType = { + id: number; + todo: string; + desc: string; + done: boolean; +}; + +export type TodoStatesType = { + todoList: Array; +}; + +const initialState: TodoStatesType = { + todoList: [ + { id: 1, todo: "데이트1", desc: "엄마랑 데이트", done: false }, + { id: 2, todo: "데이트2", desc: "아빠랑 데이트", done: false }, + { id: 3, todo: "데이트3", desc: "흰둥이랑 데이트", done: true }, + { id: 4, todo: "데이트4", desc: "동생이랑 데이트", done: false }, + ], +}; + +const TodoReducer = createReducer(initialState, (builder) => { + builder + .addCase(TodoActionCreator.addTodo, (state, action) => { + state.todoList.push({ + id: new Date().getTime(), + todo: action.payload.todo, + desc: action.payload.desc, + done: false, + }); + }) + + .addCase(TodoActionCreator.deleteTodo, (state, action) => { + let index = state.todoList.findIndex( + (item) => item.id === action.payload.id + ); + state.todoList.splice(index, 1); + }) + + .addCase(TodoActionCreator.toggleDone, (state, action) => { + let index = state.todoList.findIndex( + (item) => item.id === action.payload.id + ); + state.todoList[index].done = !state.todoList[index].done; + }) + + .addCase(TodoActionCreator.updateTodo, (state, action) => { + let index = state.todoList.findIndex( + (item) => item.id === action.payload.id + ); + state.todoList[index] = { ...action.payload }; + }) + + .addDefaultCase((state, action) => state); +}); + +export default TodoReducer; diff --git a/vite.config.ts b/vite.config.ts index 5a33944..bf592f4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,16 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; -// https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) + base: "", + server: { + proxy: { + "/api": { + target: "https://todosvc.vercel.app", + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ""), + }, + }, + }, +});