diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1031301 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["jpoissonnier.vscode-styled-components"] +} diff --git a/craco.config.js b/craco.config.js new file mode 100644 index 0000000..aec2aab --- /dev/null +++ b/craco.config.js @@ -0,0 +1,15 @@ +module.exports = { + webpack: { + configure: { + module: { + rules: [ + { + type: "javascript/auto", + test: /\.mjs$/, + include: /node_modules/, + }, + ], + }, + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index 0df0eec..abec8e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-masterclass", "version": "0.1.0", "dependencies": { + "@craco/craco": "^6.4.2", "@testing-library/jest-dom": "^5.15.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", @@ -16,18 +17,24 @@ "@types/react": "^17.0.36", "@types/react-dom": "^17.0.11", "apexcharts": "^3.31.0", + "framer-motion": "^5.4.1", + "gh-pages": "^3.2.3", "react": "^17.0.2", "react-apexcharts": "^1.3.9", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", + "react-hook-form": "^7.20.5", "react-query": "^3.33.5", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", + "recoil": "^0.5.2", "styled-components": "^5.3.3", "typescript": "^4.5.2", "web-vitals": "^1.1.2" }, "devDependencies": { + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", "@types/styled-components": "^5.1.15" @@ -1781,6 +1788,27 @@ "node": ">=0.1.95" } }, + "node_modules/@craco/craco": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", + "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "dependencies": { + "cosmiconfig": "^7.0.1", + "cross-spawn": "^7.0.0", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "webpack-merge": "^4.2.2" + }, + "bin": { + "craco": "bin/craco.js" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "react-scripts": "^4.0.0" + } + }, "node_modules/@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -3505,7 +3533,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -3743,6 +3770,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", @@ -3760,6 +3796,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/react-router": { "version": "5.1.17", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", @@ -6577,6 +6624,14 @@ "node": ">=6.0.0" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -7616,6 +7671,11 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "node_modules/email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, "node_modules/emittery": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", @@ -9210,6 +9270,30 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -9575,6 +9659,43 @@ "node": ">=0.10.0" } }, + "node_modules/framer-motion": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.4.1.tgz", + "integrity": "sha512-U4hiU3g5RCaZRTtTTOKStXH1jm+8uiIhM46wjIYQ7fWnc5c1cGlAzo238X7AM3WhU//jtqd1l5EHq32x96Eg4g==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "@react-three/fiber": "^7.0.21", + "react": ">=16.8 || ^17.0.0", + "react-dom": ">=16.8 || ^17.0.0", + "three": "^0.135.0" + }, + "peerDependenciesMeta": { + "@react-three/fiber": { + "optional": true + }, + "three": { + "optional": true + } + } + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -9735,6 +9856,144 @@ "node": ">=0.10.0" } }, + "node_modules/gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dependencies": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/gh-pages/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/gh-pages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/gh-pages/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gh-pages/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gh-pages/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/gh-pages/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -9850,6 +10109,11 @@ "node": ">=6" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -10037,6 +10301,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -13441,6 +13710,11 @@ "node": ">= 0.6" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -14843,6 +15117,17 @@ "node": ">=6" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -16361,6 +16646,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -16448,6 +16738,24 @@ "node": ">=10" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0", + "react-dom": "^16.8.5 || ^17.0.0" + } + }, "node_modules/react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -16604,6 +16912,21 @@ "react": ">=16.3.0" } }, + "node_modules/react-hook-form": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.20.5.tgz", + "integrity": "sha512-xYeBmQW6oqxDYAYVWIoZYC7tRD6lJBJt9b8Rr1Mv/VhZ+ZUCy2IuXlm2YA9i0/zl0TKKxqBWQOxGEb3qyVIhMg==", + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16634,6 +16957,41 @@ } } }, + "node_modules/react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -16928,6 +17286,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -16951,6 +17328,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -18950,6 +19335,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/style-loader": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", @@ -18969,6 +19365,15 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/styled-components": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", @@ -19746,6 +20151,17 @@ "node": ">=8" } }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -20184,6 +20600,14 @@ "node": ">=0.10.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -21348,6 +21772,14 @@ "node": ">= 4.0.0" } }, + "node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -23332,6 +23764,18 @@ "minimist": "^1.2.0" } }, + "@craco/craco": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", + "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "requires": { + "cosmiconfig": "^7.0.1", + "cross-spawn": "^7.0.0", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "webpack-merge": "^4.2.2" + } + }, "@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -24600,7 +25044,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -24800,6 +25243,15 @@ "csstype": "^3.0.2" } }, + "@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", @@ -24817,6 +25269,17 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-router": { "version": "5.1.17", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", @@ -27092,6 +27555,14 @@ "postcss": "^7.0.5" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -27900,6 +28371,11 @@ } } }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, "emittery": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", @@ -29083,6 +29559,21 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -29366,6 +29857,27 @@ "map-cache": "^0.2.2" } }, + "framer-motion": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.4.1.tgz", + "integrity": "sha512-U4hiU3g5RCaZRTtTTOKStXH1jm+8uiIhM46wjIYQ7fWnc5c1cGlAzo238X7AM3WhU//jtqd1l5EHq32x96Eg4g==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, + "framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "requires": { + "tslib": "^2.1.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -29483,6 +29995,106 @@ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, + "gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -29570,6 +30182,11 @@ "pify": "^4.0.1" } }, + "hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -29704,6 +30321,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -32253,6 +32875,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -33334,6 +33961,17 @@ "ts-pnp": "^1.1.6" } }, + "popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "requires": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -34576,6 +35214,11 @@ "performance-now": "^2.1.0" } }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -34646,6 +35289,20 @@ "whatwg-fetch": "^3.4.1" } }, + "react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "requires": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -34770,6 +35427,12 @@ "react-side-effect": "^2.1.0" } }, + "react-hook-form": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.20.5.tgz", + "integrity": "sha512-xYeBmQW6oqxDYAYVWIoZYC7tRD6lJBJt9b8Rr1Mv/VhZ+ZUCy2IuXlm2YA9i0/zl0TKKxqBWQOxGEb3qyVIhMg==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -34785,6 +35448,29 @@ "match-sorter": "^6.0.2" } }, + "react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -35031,6 +35717,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "requires": { + "hamt_plus": "1.0.2" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -35048,6 +35742,14 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -36648,6 +37350,14 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "style-loader": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", @@ -36657,6 +37367,15 @@ "schema-utils": "^2.7.0" } }, + "style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "styled-components": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", @@ -37244,6 +37963,14 @@ "punycode": "^2.1.1" } }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -37577,6 +38304,12 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "requires": {} + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -38746,6 +39479,14 @@ } } }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "requires": { + "lodash": "^4.17.15" + } + }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", diff --git a/package.json b/package.json index 8ac911a..cf87862 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@craco/craco": "^6.4.2", "@testing-library/jest-dom": "^5.15.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", @@ -11,22 +12,29 @@ "@types/react": "^17.0.36", "@types/react-dom": "^17.0.11", "apexcharts": "^3.31.0", + "framer-motion": "^5.4.1", + "gh-pages": "^3.2.3", "react": "^17.0.2", "react-apexcharts": "^1.3.9", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", + "react-hook-form": "^7.20.5", "react-query": "^3.33.5", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", + "recoil": "^0.5.2", "styled-components": "^5.3.3", "typescript": "^4.5.2", "web-vitals": "^1.1.2" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" + "start": "craco start", + "build": "craco build", + "test": "craco test", + "eject": "react-scripts eject", + "deploy": "gh-pages -d build", + "predeploy": "npm run build" }, "eslintConfig": { "extends": [ @@ -47,8 +55,10 @@ ] }, "devDependencies": { + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", "@types/styled-components": "^5.1.15" - } + }, + "homepage": "https://gilpop8663.github.io/reactmaster" } diff --git a/public/index.html b/public/index.html index aa069f2..cd1454b 100644 --- a/public/index.html +++ b/public/index.html @@ -2,7 +2,10 @@ - + - React App + Nomflix diff --git a/src/App.tsx b/src/App.tsx index 03074c7..6225adb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,73 +1,36 @@ -import Router from "./Router"; -import { createGlobalStyle } from "styled-components"; -import { ReactQueryDevtools } from "react-query/devtools"; - -const GlobalStyle = createGlobalStyle` -@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400&display=swap'); -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} -*{ - box-sizing: border-box; -} -a{ - text-decoration: none; - color:inherit; -} - -body{ - font-family: 'Source Sans Pro', sans-serif; - background-color: ${(props) => props.theme.bgColor}; - color:${(props) => props.theme.textColor} -} -`; +import { Helmet } from "react-helmet"; +import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; +import Header from "./Components/Header"; +import Home from "./Routes/Home"; +import Search from "./Routes/Search"; +// import Search from "./Routes/Search"; +import Tv from "./Routes/Tv"; function App() { return ( <> - - - + + + + +
+ + + + + + + + + + + + ); } diff --git a/src/Components/BannerScreen.tsx b/src/Components/BannerScreen.tsx new file mode 100644 index 0000000..d98f847 --- /dev/null +++ b/src/Components/BannerScreen.tsx @@ -0,0 +1,129 @@ +import { motion } from "framer-motion"; +import { useState } from "react"; +import { useQuery } from "react-query"; +import styled from "styled-components"; +import { getVideoDetail, IGetVideoDetail, IGetVideosProps } from "../api"; +import { makeImageHelper } from "../utils"; + +const Banner = styled(motion.iframe)<{ + bgPhoto: string; + userWidth: number; + over?: boolean; +}>` + background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)), + url(${(props) => props.bgPhoto}); + background-size: cover; + display: flex; + flex-direction: column; + height: 100vh; + width: 100%; + aspect-ratio: 16/9; + background-position: center center; + z-index: 1; +`; + +const Wrapper = styled.div` + flex-direction: column; + top: 40vh; + display: flex; + justify-content: center; + align-content: center; + position: absolute; + padding: 60px; + z-index: 0; +`; + +const Title = styled.h2` + font-size: 64px; + margin-bottom: 20px; +`; + +const Overview = styled.p` + font-size: 34px; + width: 80%; +`; + +interface IBanner { + videoData: IGetVideosProps; + isWhat: string; +} + +function BannerScreen({ videoData, isWhat }: IBanner) { + const [over, setOver] = useState(false); + + const detailData = useQuery(["video", "detail"], () => + getVideoDetail("movie", videoData.results[0].id + "") + ); + //console.log(detailData.data?.videos.results); + const teasearVideo = detailData.data?.videos.results.find( + (item) => item.type === "Teaser" + ); + const trailerVideo = detailData.data?.videos.results.find( + (item) => item.type === "Trailer" + ); + const openingVideo = detailData.data?.videos.results.find( + (item) => item.type === "Opening Credits" + ); + + const detailVideo = teasearVideo + ? teasearVideo + : trailerVideo + ? trailerVideo + : openingVideo; + const mouseEnter = (event: any) => { + setOver(true); + }; + const mouseLeave = () => { + setOver(false); + }; + return ( + <> + {detailVideo ? ( + + ) : ( + + )} + {!over && ( + + + {isWhat === "movie" && videoData.results[0].title} + {isWhat === "tv" && videoData.results[0].name} + + {videoData.results[0].overview} + + )} + + ); +} + +export default BannerScreen; diff --git a/src/Components/ClickMovie.tsx b/src/Components/ClickMovie.tsx new file mode 100644 index 0000000..abb0c15 --- /dev/null +++ b/src/Components/ClickMovie.tsx @@ -0,0 +1,762 @@ +import { + AnimatePresence, + motion, + useViewportScroll, + Variants, +} from "framer-motion"; +import { useEffect, useState } from "react"; +import { useQuery } from "react-query"; +import { useHistory, useLocation, useRouteMatch } from "react-router-dom"; +import styled from "styled-components"; +import { + getSimilarData, + getVideoCredit, + getVideoDetail, + IGetVideoDetail, + IMovie, + ISimilarProps, + IVideoCredit, +} from "../api"; +import { makeImageHelper } from "../utils"; + +const Loader = styled.div` + width: 100%; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + font-size: 48px; +`; + +const Overlay = styled(motion.div)` + position: fixed; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.3); + opacity: 0; +`; + +const BigMovie = styled(motion.div)` + position: absolute; + width: 40vw; + left: 0; + right: 0; + margin: 0 auto; + overflow: hidden; + border-radius: 15px; + min-width: 426px; + background-color: ${(props) => props.theme.black.veryDark}; +`; + +const DisableCover = styled.div<{ isOver: boolean }>` + width: 100%; + height: 400px; + position: absolute; + top: 0; + left: 0; + right: 0; + margin: 0 auto; + z-index: ${(props) => (props.isOver ? 100 : -2)}; +`; + +const BigCover = styled(motion.iframe)<{ bgPhoto?: string; userWidth: number }>` + background-image: linear-gradient(to top, black, transparent), + url(${(props) => props.bgPhoto}); + background-size: cover; + top: 0; + right: 0; + left: 0; + margin: 0 auto; + aspect-ratio: 16/9; + width: 100%; + background-position: center center; + height: ${(props) => + props.userWidth > 3840 + ? 2160 + : props.userWidth > 2560 + ? 1440 + : props.userWidth > 1920 + ? 1080 + : props.userWidth > 1280 + ? 720 + : props.userWidth > 854 + ? 480 + : props.userWidth > 640 + ? 360 + : 240}; + + border: none; +`; + +const BigContainer = styled(motion.div)` + padding: 50px; + top: -230px; + position: relative; +`; + +const BigTitle = styled.h3` + color: ${(props) => props.theme.white.lighter}; + font-size: 48px; + position: relative; + margin-bottom: 50px; +`; + +const BigOverview = styled.p` + color: ${(props) => props.theme.white.lighter}; + position: relative; + margin-bottom: 100px; + font-size: 19px; + font-weight: 400; + line-height: 25px; + width: 80%; +`; + +const DetailGrid = styled.div` + display: grid; + grid-template-columns: 5fr 2fr; +`; + +const DetailBox = styled.div` + display: flex; + flex-direction: column; +`; + +const UserBox = styled.div` + display: flex; + align-items: center; + margin-bottom: 60px; +`; + +const PlayCircle = styled.div` + width: 40px; + margin-left: 10px; + height: 40px; + border-radius: 20px; + border: 2px solid ${(props) => props.theme.white.darker}; + display: flex; + justify-content: center; + align-items: center; + color: ${(props) => props.theme.white.lighter}; + font-size: 18px; + font-weight: 100; + i { + display: flex; + justify-content: center; + align-items: center; + } +`; + +const Playbox = styled.div` + width: 150px; + display: flex; + height: 40px; + justify-content: center; + align-items: center; + color: black; + font-size: 18px; + font-weight: 600; + border-radius: 5px; + background-color: ${(props) => props.theme.white.lighter}; + i { + margin-right: 10px; + } +`; + +const InfoBox = styled.div` + display: flex; + align-items: flex-start; + flex-wrap: wrap; + margin-bottom: 20px; +`; + +const InfoSpan = styled.div` + font-size: 14px; + color: ${(props) => props.theme.white.veryDark}; +`; + +const MovieInfoTop = styled.span` + color: ${(props) => props.theme.white.darker}; + margin-left: 5px; + font-size: 16px; + font-weight: 400; +`; + +const MovieInfo = styled.span` + color: ${(props) => props.theme.white.darker}; + margin-left: 5px; + display: flex; + line-height: 18px; + align-items: center; +`; + +const MoiveAverage = styled.span` + font-weight: 600; + color: #42c262; + font-size: 16px; + margin-bottom: 10px; +`; + +const MoreBox = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; + grid-template-rows: repeat(3, minmax(100px, 1fr)); + width: 100%; +`; + +const MoreMovie = styled.div` + cursor: pointer; + width: 100%; + border-radius: 10px; + + background-color: ${(props) => props.theme.black.lighter}; + color: ${(props) => props.theme.white.darker}; +`; +const MoreCover = styled.div<{ bgPhoto: string }>` + background-image: url(${(props) => props.bgPhoto}); + background-position: center center; + background-size: cover; + height: 180px; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + margin-bottom: 10px; +`; + +const MoreInfoBox = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + padding: 15px; +`; + +const MoreInfo = styled.span` + display: flex; + margin-bottom: 10px; + font-size: 14px; + align-items: center; + line-height: 18px; +`; + +const MoreTitle = styled.span` + margin-bottom: 10px; + display: flex; + font-weight: 600; + font-size: 16px; + line-height: 20px; + color: ${(props) => props.theme.white.lighter}; +`; + +interface IClickMovie { + bigVideoMatch: { params?: { movieId?: string | null; tvId?: string | null } }; + videoData: IMovie[]; + isWhat: string; + search?: string; + keyword?: string; + unqKey: string; +} + +const opacityV: Variants = { + normal: { + opacity: 1, + }, + entry: { + opacity: 0, + }, + exit: { + opacity: 0, + }, +}; + +function ClickMovie({ + bigVideoMatch, + isWhat, + videoData, + search, + keyword, + unqKey, +}: IClickMovie) { + //console.log(videoData); + const history = useHistory(); + const { scrollY } = useViewportScroll(); + const [over, setOver] = useState(false); + const [clicked, setClicked] = useState(false); + useEffect(() => { + setClicked(false); + }, [clicked]); + // console.log(history); + const location = useLocation(); + const movieKeyword: string = new URLSearchParams(location.search).get( + "movies" + ) + ? String(new URLSearchParams(location.search).get("movies")) + : ""; + + const tvKeyword: string = new URLSearchParams(location.search).get("tv") + ? String(new URLSearchParams(location.search).get("tv")) + : ""; + + const searchKeyword = movieKeyword ? movieKeyword : tvKeyword; + + const movieSearch = bigVideoMatch?.params?.movieId + ? bigVideoMatch?.params.movieId + : searchKeyword; + + // console.log(movieSearch); + + const tvSearch = bigVideoMatch?.params?.tvId + ? bigVideoMatch.params.tvId + : searchKeyword; + + const isSearch = movieSearch ? movieSearch : tvSearch; + // console.log( + // `isSearch: ${isSearch}, movieSearch : ${movieSearch}, tvSearch : ${tvSearch}` + // ); + //console.log(isSearch); + const backUrl = `${location.pathname}?keyword=${keyword}`; + //console.log(location.pathname); + // console.log(backUrl); + const onOverlayClicked = () => { + setOver(false); + if (!search) { + if (isWhat === "movie") { + history.push("/"); + } else if (isWhat === "tv") { + history.push("/tv"); + } + } else { + history.push(`${backUrl}`); + } + //history.push(location); + }; + + const creditData = useQuery(["video", "credit"], () => + getVideoCredit(isWhat, isSearch) + ); + + const similarData = useQuery(["video", "similar"], () => + getSimilarData(isWhat, isSearch) + ); + + //console.log(similarData.data?.results); + //console.log(clicked); + // const { data } = useQuery(["movies", "video"], () => + // getVideo(bigVideoMatch=.params.movieId) + // ); + + //console.log(similarMovieData); + // console.log(isSearch, creditData.data); + //console.log(similarData.data?.results[0].id); + // const clickedData = + // bigVideoMatch=params.movieId && + // movieData.find((item) => item.id === +bigVideoMatch=params.movieId) + // ? movieData.find((item) => item.id === +bigVideoMatch=params.movieId) + // : similarData?.data?.results?.find( + // (item) => item.id === +bigVideoMatch=params.movieId + // ); + const similarMovieMatch = useRouteMatch<{ movieId: string }>( + !search ? `/movies/:movieId` : `undefined` + ); + + const similarTvMatch = useRouteMatch<{ tvId: string }>( + !search ? `/tv/:tvId` : `undefined` + ); + + const locationTv = { + params: { + tvId: new URLSearchParams(location.search).get("tv"), + }, + }; + // console.log(locationTv); + const locationMovie = { + params: { + movieId: new URLSearchParams(location.search).get("movies"), + }, + }; + + const similarMatch = similarMovieMatch ? similarMovieMatch : similarTvMatch; + + const detailData = useQuery(["videos", "detail"], () => + getVideoDetail(isWhat, isSearch) + ); + console.log(isSearch); + const clickedData = videoData.find((item) => item.id === +isSearch) + ? videoData.find((item) => item.id === +isSearch) + : detailData.data; + + const teasearVideo = detailData.data?.videos.results.find( + (item) => item.type === "Teaser" + ); + const trailerVideo = detailData.data?.videos.results.find( + (item) => item.type === "Trailer" + ); + + const openingVideo = detailData.data?.videos.results.find( + (item) => item.type === "Opening Credits" + ); + + const detailVideo = teasearVideo + ? teasearVideo + : trailerVideo + ? trailerVideo + : openingVideo; + // console.log(clickedData); + const mouseEnter = (event: any) => { + setOver(true); + }; + //console.log(similarMatch, similarMovieMatch, similarTvMatch); + //console.log(similarMovieMatch, similarMatch); + //console.log(similarTvMatch); + //console.log(data, isLoading); + //console.log(detailData.data); + //console.log(similarData.data?.results[0]); + //console.log(bigVideoMatch=params.movieId); + const onBoxClicked = (id: number) => { + setOver(false); + setClicked(true); + if (!search) { + if (isWhat === "movie") { + history.push(`/movies/${id}`); + } else if (isWhat === "tv") { + history.push(`/tv/${id}`); + } + } else if (search) { + if (isWhat === "movie") { + history.push(`${location.pathname}?keyword=${keyword}&movies=${id}`); + } else if (isWhat === "tv") { + history.push(`${location.pathname}?keyword=${keyword}&tv=${id}`); + } + } + }; + + // console.log(detailData.data?.videos.results[0].type); + //console.log(clickedData?.poster_path); + //console.log(clickedData); + // console.log("무비ㅏ아이디임", movieId); + // console.log( + // "시밀러 데이터 :", + // similarData.data?.results, + // "클릭임:", + // clicked, + // "시밀러맷치", + // similarMatch + // ); + //console.log(clickedData); + return ( + <> + {detailData.isLoading ? ( + Loading... + ) : ( + <> + + {clickedData && ( + + )} + {clickedData && ( + + <> + {detailVideo ? ( + + ) : ( + + )} + + + + {isWhat === "movie" + ? clickedData.title + : clickedData.name} + + + + + 재생 + + + + + + + + + + + + + + + + {clickedData.vote_average + ? `${(clickedData.vote_average * 10).toFixed( + 0 + )}% 일치` + : ""} + + + {isWhat === "movie" && clickedData?.release_date + ? clickedData?.release_date.slice(0, 4) + : clickedData.first_air_date?.slice(0, 4)} + 년 + + + {detailData.data?.runtime === 0 || + detailData.data?.runtime === null || + detailData.data?.runtime === undefined + ? "" + : `${detailData.data?.runtime}분`} + + + + {clickedData.overview} + + + {detailData.data?.genres && + creditData.data?.crew && + creditData.data?.cast && ( + + + + 장르: + + {detailData.data?.genres?.map((item: any) => ( + + {item.name}, + + ))} + + + + + 출연진: + + + {creditData.data?.cast[0] + ? `${creditData.data?.cast[0].name},` + : ""} + + + {creditData.data?.cast[1] + ? `${creditData.data?.cast[1].name},` + : ""} + + + {creditData.data?.cast[2] + ? `${creditData.data?.cast[2].name}` + : ""} + + + + + 크리에이터: + + + {creditData.data?.crew[0] + ? `${creditData.data?.crew[0].name},` + : ""} + + + {creditData.data?.crew[1] + ? `${creditData.data?.crew[1].name},` + : ""} + + + {creditData.data?.crew[2] + ? `${creditData.data?.crew[2].name}` + : ""} + + + + )} + + + {similarData?.data?.results && ( + + {similarData.data?.results[9] && + Array.from({ length: 9 }, (v, i) => i).map((item) => ( + + onBoxClicked( + similarData?.data + ? similarData?.data?.results[item].id + : 0 + ) + } + > + {(similarMatch || search) && + similarData.data?.results && ( + + )} + + + {isWhat === "movie" + ? similarData.data?.results[item].title + : similarData.data?.results[item].name} + + + {similarData.data?.results[item].vote_average + ? `${( + similarData.data?.results[item] + .vote_average * 10 + ).toFixed(0)}%일치` + : ""} + + + {similarData.data?.results[item].overview && ( + + {similarData.data?.results[item].overview + .length > 200 + ? `${similarData.data?.results[ + item + ].overview.slice(0, 200)}...` + : similarData.data?.results[item] + .overview} + + )} + + + ))} + + )} + + + + )} + ) + + <> + {similarData.data?.results && + clicked && + (similarMovieMatch || + (locationMovie?.params?.movieId + ? locationMovie?.params?.movieId === isSearch + : false) ? ( + + ) : ( + similarData.data?.results && + clicked && + (similarTvMatch || + (locationTv?.params?.tvId + ? locationTv?.params?.tvId === isSearch + : false)) && ( + + ) + ))} + + ) + + )} + + ); +} + +export default ClickMovie; diff --git a/src/Components/Footer.tsx b/src/Components/Footer.tsx new file mode 100644 index 0000000..f16c6c6 --- /dev/null +++ b/src/Components/Footer.tsx @@ -0,0 +1,85 @@ +import styled from "styled-components"; + +const Wrapper = styled.div` + width: 100vw; + height: 20vh; + display: flex; + background-color: ${(props) => props.theme.black.darker}; + flex-direction: column; + justify-content: center; + align-content: center; +`; + +const Copy = styled.span` + text-align: center; +`; + +const Icons = styled.div` + margin-top: 15px; + display: flex; + justify-content: space-between; + align-content: center; + height: 5vh; + padding-left: 45vw; + padding-right: 45vw; +`; +const Icon = styled.a` + display: flex; + flex-direction: column; + height: 30px; + justify-content: center; + align-content: center; + width: 30px; + &:hover { + } + + span { + text-align: center; + } + i { + display: flex; + justify-content: center; + align-content: center; + } +`; + +const NomarImg = styled.img` + height: 20px; + display: flex; + justify-content: center; + align-content: center; +`; + +function Footer() { + return ( + + © 2021 - Kim Young Gil, All rights reserved. + + + + + + + + + + + + + ); +} + +export default Footer; diff --git a/src/Components/Header.tsx b/src/Components/Header.tsx new file mode 100644 index 0000000..ea33498 --- /dev/null +++ b/src/Components/Header.tsx @@ -0,0 +1,187 @@ +import { + motion, + useAnimation, + useViewportScroll, + Variants, +} from "framer-motion"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useHistory, useRouteMatch } from "react-router"; +import { Link } from "react-router-dom"; +import styled from "styled-components"; + +const Nav = styled(motion.div)` + display: flex; + justify-content: space-between; + align-items: center; + position: fixed; + width: 100%; + top: 0; + background-color: black; + font-size: 14px; + padding: 20px 60px; + color: white; + z-index: 2; +`; + +const Col = styled.div` + display: flex; + align-items: center; +`; + +const Logo = styled(motion.svg)` + margin-right: 50px; + width: 95px; + height: 25px; + fill: ${(props) => props.theme.red}; + + path { + stroke-width: 6px; + stroke: white; + } +`; + +const Circle = styled(motion.span)` + width: 5px; + height: 5px; + border-radius: 2.5px; + background-color: ${(props) => props.theme.red}; + position: absolute; + bottom: -10px; + left: 0; + right: 0; + margin: 0 auto; +`; + +const Items = styled.ul` + display: flex; + align-items: center; +`; + +const Item = styled.li` + margin-right: 20px; + color: ${(props) => props.theme.white.darker}; + transition: color 0.3s ease-in-out; + position: relative; + display: flex; + justify-content: center; + flex-direction: column; + &:hover { + color: ${(props) => props.theme.white.lighter}; + } +`; + +const Search = styled.form` + color: white; + position: relative; + display: flex; + align-items: center; + svg { + height: 25px; + } +`; + +const Input = styled(motion.input)` + transform-origin: right center; + position: absolute; + padding: 5px 10px; + right: 0; + padding-left: 40px; + z-index: -1; + color: white; + font-size: 16px; + background-color: transparent; + border: 1px solid ${(props) => props.theme.white.lighter}; +`; + +const LogoVariants: Variants = { + normal: { fillOpacity: 1 }, + active: { fillOpacity: [0, 1, 0], transition: { repeat: Infinity } }, +}; + +interface IForm { + keyword: string; +} + +function Header() { + const { register, handleSubmit } = useForm(); + const history = useHistory(); + const [openSearch, setOpenSearch] = useState(false); + const homeMatch = useRouteMatch("/"); + const tvMatch = useRouteMatch("/tv"); + const navAnimation = useAnimation(); + // console.log(homeMatch, tvMatch); + const { scrollY } = useViewportScroll(); + const toggleSearch = () => { + setOpenSearch((current) => !current); + }; + useEffect(() => { + scrollY.onChange(() => { + if (scrollY.get() > 80) { + navAnimation.start({ backgroundColor: "rgba(0,0,0,1)" }); + } else { + navAnimation.start({ backgroundColor: "rgba(0,0,0,0)" }); + } + }); + }, [scrollY, navAnimation]); + const onValid = (data: IForm) => { + //console.log(data); + history.push(`/search?keyword=${data.keyword}`); + }; + return ( + + ); +} + +export default Header; diff --git a/src/Components/NowMovies.tsx b/src/Components/NowMovies.tsx new file mode 100644 index 0000000..79f4f3c --- /dev/null +++ b/src/Components/NowMovies.tsx @@ -0,0 +1,429 @@ +import { AnimatePresence, motion, Variants } from "framer-motion"; +import React, { useState } from "react"; +import { useHistory, useLocation, useRouteMatch } from "react-router-dom"; +import styled from "styled-components"; +import { IMovie } from "../api"; +import { makeImageHelper } from "../utils"; +import ClickMovie from "./ClickMovie"; + +const Slider = styled.div` + position: relative; + height: 180px; + top: -170px; + margin-bottom: 70px; +`; + +const Row = styled(motion.div)<{ isFirst: number; hover: boolean }>` + display: grid; + grid-template-columns: ${(props) => + props.hover === false + ? "repeat(6, 1fr) 0.2fr" + : "0.2fr repeat(6, 1fr) 0.2fr"}; + gap: 5px; + width: 100%; + margin-top: 16px; + padding: ${(props) => + props.hover === false ? "0px 0px 0px 65px" : "0px 0px 0px 0px"}; + position: absolute; +`; + +const Box = styled(motion.div)<{ hover?: boolean; bgPhoto: string }>` + background-color: white; + height: 180px; + font-size: 66px; + background-image: ${(props) => + props.bgPhoto === `https://image.tmdb.org/t/p/w500/` + ? `url( + "https://img.freepik.com/vector-gratis/signo-exclamacion-blanco-circulo-rojo-aislado-sobre-fondo-blanco_120819-332.jpg?size=338&ext=jpg" + )` + : `url(${props.bgPhoto})`}; + background-position: center center; + background-size: cover; + cursor: pointer; + &:hover { + z-index: 2; + } + &:first-child { + transform-origin: ${(props) => + props.hover ? "center left" : "center left"}; + } + &:nth-child(2) { + transform-origin: ${(props) => + props.hover ? "center left" : "center center"}; + } + &:nth-child(6) { + transform-origin: ${(props) => + props.hover ? "center center" : "center right"}; + } + &:nth-child(7) { + transform-origin: ${(props) => + props.hover ? "center right" : "center center"}; + } +`; + +const Archive = styled.span` + font-size: 32px; + font-weight: 600; + padding-left: 65px; +`; + +const ArrowBtn = styled(motion.div)` + color: white; + z-index: 1; + width: 65px; + height: 185px; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + bottom: -50px; + i { + position: relative; + text-align: center; + } + + &:first-child { + left: 0; + } + &:last-child { + right: 0; + } +`; + +const Info = styled(motion.div)` + padding: 10px; + background-color: ${(props) => props.theme.black.lighter}; + position: absolute; + width: 100%; + bottom: 0; + opacity: 0; + h4 { + font-size: 14px; + text-align: center; + } +`; + +const ArchiveContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: flex-end; +`; + +const IndexBoxs = styled.div` + display: flex; + padding-right: 65px; +`; + +const IndexBox = styled(motion.div)<{ index: boolean }>` + width: 10px; + margin-left: 2px; + height: 2px; + background-color: ${(props) => + props.index ? props.theme.white.lighter : props.theme.black.lighter}; +`; + +const rowVariants: Variants = { + hidden: (back: boolean) => ({ + x: back ? window.outerWidth + 5 : -window.outerWidth - 5, + }), + visible: { x: 0 }, + exit: (back: boolean) => ({ + x: back ? -window.outerWidth - 5 : window.outerWidth + 5, + }), +}; + +const boxVariants: Variants = { + normal: { scale: 1, transition: { type: "tween" } }, + hover: { + scale: 1.3, + y: -50, + transition: { delay: 0.5, duration: 0.3, type: "tween" }, + }, +}; + +const infoVariants: Variants = { + hover: { + opacity: 1, + transition: { delay: 0.5, duration: 0.1, type: "tween" }, + }, +}; + +const ArrowVariants: Variants = { + btnNormal: { + backgroundColor: "rgba(0,0,0,0.5)", + fontSize: "20px", + color: "rgba(255,255,255,0)", + }, + btnHover: { + backgroundColor: "rgba(0, 0, 0, 0.9)", + fontSize: "30px", + color: "rgba(255,255,255,1)", + }, + exit: { + opacity: 0, + }, +}; + +const offset = 6; + +interface INowProps { + videoData: IMovie[]; + sliderTitle?: string; + search?: string; + isWhat: string; + unqKey: string; +} + +function NowMovies({ + videoData, + sliderTitle, + search, + isWhat, + unqKey, +}: INowProps) { + const history = useHistory(); + const location = useLocation(); + //console.log(pathName); + const [leaving, setLeaving] = useState(false); + const [rowHover, setRowHover] = useState(false); + const rowMouseEnterLeave = () => { + setRowHover((prev) => !prev); + }; + const toggleLeaving = () => { + setLeaving((prev) => !prev); + }; + const [back, setBack] = useState(false); + const [hover, setHover] = useState(false); + const [index, setIndex] = useState(0); + const totalMovies = videoData.length - 1; + const maxIndex = Math.floor(totalMovies / offset) - 1; + const decreaseIndex = () => { + if (videoData) { + if (leaving) return; + setBack(false); + toggleLeaving(); + + setIndex((prev) => (index === 0 ? maxIndex : prev - 1)); + } + }; + const increaseIndex = () => { + if (videoData) { + if (leaving) return; + setBack(true); + toggleLeaving(); + setHover(true); + console.log("맥스인덱스 : ", maxIndex, "인덱스 : ", index); + setIndex((prev) => (index === maxIndex ? 0 : prev + 1)); + } + }; + let keyword = new URLSearchParams(location.search).get("keyword"); + const onBoxClicked = (id: number) => { + //console.log(id); + if (!search) { + if (isWhat === "movie") { + history.push(`/movies/${id}`); + } else if (isWhat === "tv") { + history.push(`/tv/${id}`); + } + } else if (search) { + if (isWhat === "movie") { + history.push(`/search?keyword=${keyword}&movies=${id}`); + } else if (isWhat === "tv") { + history.push(`/search?keyword=${keyword}&tv=${id}`); + } + } + }; + // console.log(isSearch); + const bigMovieMatch = useRouteMatch<{ movieId?: string }>( + !search ? `/movies/:movieId` : "undefined" + ); + + const bigTvMatch = useRouteMatch<{ tvId?: string }>( + !search ? `/tv/:tvId` : "undefined" + ); + + const locationTv = { + params: { + tvId: new URLSearchParams(location.search).get("tv"), + }, + }; + // console.log(locationTv); + const locationMovie = { + params: { + movieId: new URLSearchParams(location.search).get("movies"), + }, + }; + + //console.log(locationTv, locationMovie); + + return ( + <> + + + + {sliderTitle} + + {rowHover && + Array.from({ length: maxIndex + 1 }, (v, i) => i).map( + (item) => ( + + ) + )} + + + {hover && ( + + + + )} + + {hover === true ? ( + + ) : null} + {videoData + .slice(1) + .slice(offset * index, offset * index + offset) + .map((item) => ( + onBoxClicked(item.id)} + hover={hover} + variants={boxVariants} + initial="normal" + whileHover="hover" + transition={{ type: "tween", duration: 0.1 }} + bgPhoto={makeImageHelper( + item.backdrop_path + ? item.backdrop_path + : item.poster_path + ? item.poster_path + : "", + "w500" + )} + key={item.id + unqKey} + > + +

+ {isWhat === "movie" ? item.title : item.name} +

+
+
+ ))} + {index !== maxIndex ? ( + + ) : ( + + )} +
+ ( + + + + ) +
+
+ {(locationMovie.params.movieId + ? locationMovie.params.movieId?.length >= 1 + : false || bigMovieMatch) && + isWhat === "movie" && ( + + )} + {(locationTv.params.tvId + ? locationTv.params.tvId?.length >= 1 + : false || bigTvMatch) && + isWhat === "tv" && ( + + )} + + ); +} + +export default React.memo(NowMovies); diff --git a/src/Router.tsx b/src/Router.tsx deleted file mode 100644 index 8c86fa2..0000000 --- a/src/Router.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { BrowserRouter, Switch, Route } from "react-router-dom"; -import Coin from "./Routes/Coin"; -import Coins from "./Routes/Coins"; - -function Router() { - return ( - - - - - - - - - - - ); -} - -export default Router; diff --git a/src/Routes/Chart.tsx b/src/Routes/Chart.tsx deleted file mode 100644 index 1635057..0000000 --- a/src/Routes/Chart.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useQuery } from "react-query"; -import { fetchOHLCInfo } from "../api"; -import ApexChart from "react-apexcharts"; -import styled from "styled-components"; - -interface ChartProps { - coinId: string; -} - -interface IHistorical { - time_open: string; - time_close: string; - open: number; - high: number; - low: number; - close: number; - volume: number; - market_cap: number; -} - -const Container = styled.div` - margin-top: 30px; -`; - -function Chart({ coinId }: ChartProps) { - const { isLoading, data } = useQuery( - ["ohlc", coinId], - () => fetchOHLCInfo(coinId), - { - refetchInterval: 10000, - } - ); - // console.log( - // data?.map((item) => [item.open, item.high, item.low, item.close]) - // ); - // console.log(data?.map((item) => item.time_close.slice(5, 10))); - //console.log(data?.map((item) => item.time_close[1])); - //console.log(data); - const arr = () => - data?.map((item) => { - let x = item.time_close.slice(5, 10); - let y = [ - item.open.toFixed(2), - item.high.toFixed(2), - item.low.toFixed(2), - item.close.toFixed(2), - ]; - //console.log(x); - return { x: x, y: y }; - }); - // console.log(arr()); - return ( - - {isLoading ? ( - "Loading Chart ..." - ) : ( - - )} - - ); -} - -export default Chart; diff --git a/src/Routes/Coin.tsx b/src/Routes/Coin.tsx deleted file mode 100644 index 2e53017..0000000 --- a/src/Routes/Coin.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "react-query"; -import { Helmet } from "react-helmet"; -import { - Route, - Switch, - useLocation, - useParams, - useRouteMatch, -} from "react-router"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { fetchCoinInfo, fetchTickersInfo } from "../api"; -import Chart from "./Chart"; -import Price from "./Price"; - -interface routeParams { - coinId: string; -} - -const Container = styled.div` - padding: 0px 20px; - max-width: 480px; - margin: 0 auto; -`; - -const Loading = styled.span` - text-align: center; - display: block; -`; - -const Header = styled.header` - display: flex; - justify-content: space-between; - align-items: center; - height: 15vh; -`; - -const Title = styled.div` - font-size: 48px; - color: ${(props) => props.theme.accentColor}; - text-align: center; -`; - -interface RouteState { - name: string; -} - -interface ICoinInfo { - id: string; - name: string; - symbol: string; - rank: number; - is_new: boolean; - is_active: boolean; - type: string; - contract: string; - platform: string; - description: string; - message: string; - open_source: boolean; - started_at: string; - development_status: string; - hardware_wallet: boolean; - proof_type: string; - org_structure: string; - hash_algorithm: string; - first_data_at: string; - last_data_at: string; -} - -interface ITickersInfo { - id: string; - name: string; - symbol: string; - rank: number; - circulating_supply: number; - total_supply: number; - max_supply: number; - beta_value: number; - first_data_at: string; - last_updated: string; - quotes: { - USD: { - ath_date: string; - ath_price: number; - market_cap: number; - market_cap_change_24h: number; - percent_change_1h: number; - percent_change_1y: number; - percent_change_6h: number; - percent_change_7d: number; - percent_change_12h: number; - percent_change_15m: number; - percent_change_24h: number; - percent_change_30d: number; - percent_change_30m: number; - percent_from_price_ath: number; - price: number; - volume_24h: number; - volume_24h_change_24h: number; - }; - }; -} - -const ContentContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; -`; - -const Article = styled.div` - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => props.theme.bgColor}; - padding: 20px 20px; - border-radius: 15px; - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 30px; -`; - -const MiniContentDiv = styled.div` - display: flex; - align-items: center; - flex-direction: column; -`; - -const ContentTitle = styled.span` - font-size: 12px; - text-align: center; - margin-bottom: 5px; -`; - -const ContentText = styled.span` - font-size: 16px; - text-align: center; - line-height: 20px; -`; - -const Tab = styled.div` - width: 100%; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; -`; - -const Tabs = styled.span<{ isActive: boolean }>` - text-align: center; - font-size: 16px; - padding: 10px; - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => - props.isActive ? props.theme.accentColor : props.theme.bgColor}; - border-radius: 10px; - a { - display: block; - } -`; - -const Home = styled.div` - text-align: center; - color: ${(props) => props.theme.bgColor}; - background-color: ${(props) => props.theme.textColor}; - border-radius: 5px; - padding: 10px; - right: 200px; - &:hover { - color: ${(props) => props.theme.accentColor}; - transition: color 0.2s ease-in-out; - } - a { - display: block; - } -`; - -function Coin() { - const { coinId } = useParams(); - const { state } = useLocation(); - const priceMatch = useRouteMatch(`/${coinId}/price`); - const chartMatch = useRouteMatch(`/${coinId}/chart`); - const { isLoading: CoinInfoLoading, data: CoinInfoData } = - useQuery(["coinInfo", coinId], () => fetchCoinInfo(coinId), { - refetchInterval: 10000, - }); - const { isLoading: TickersInfoLoading, data: TickersInfoData } = - useQuery( - ["tickersInfo", coinId], - () => fetchTickersInfo(coinId), - { - refetchInterval: 10000, - } - ); - const loading = CoinInfoLoading || TickersInfoLoading; - /* - const [loading, setLoading] = useState(true); - const [info, setInfo] = useState(); - const [priceInfo, setPriceInfo] = useState(); - console.log(chartMatch); - useEffect(() => { - (async () => { - const infoData = await ( - await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`) - ).json(); - const priceData = await ( - await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`) - ).json(); - setInfo(infoData); - setPriceInfo(priceData); - setLoading(false); - // console.log(state); - // console.log(coinId); - })(); - }, []);*/ - return ( - - - - {state?.name - ? state.name - : loading - ? "Loading..." - : CoinInfoData?.name} - - -
- - Home - - - {state?.name - ? state.name - : loading - ? "Loading..." - : CoinInfoData?.name} - -
-
-
- {loading ? ( - Loading... - ) : ( - -
- - RANK: - {CoinInfoData?.rank} - - - SYMBOL: - {CoinInfoData?.symbol} - - - OPEN SOURCE: - - {CoinInfoData?.open_source ? "Yes" : "No"} - - -
-
- {CoinInfoData?.description} -
-
- - TOTAL SUPPLY: - {TickersInfoData?.total_supply} - - - MAX SUPPLY: - {TickersInfoData?.max_supply} - -
- - - Chart - - - Price - - - - - - - - - - -
- )} -
- ); -} - -export default Coin; diff --git a/src/Routes/Coins.tsx b/src/Routes/Coins.tsx deleted file mode 100644 index f591d08..0000000 --- a/src/Routes/Coins.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "react-query"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { fetchCoins } from "../api"; -import { Helmet } from "react-helmet"; - -const Container = styled.div` - padding: 0px 20px; - max-width: 480px; - margin: 0 auto; -`; - -const Loading = styled.span` - text-align: center; - display: block; -`; - -const Header = styled.header` - display: flex; - justify-content: center; - align-items: center; - height: 15vh; -`; - -const Title = styled.div` - font-size: 48px; - color: ${(props) => props.theme.accentColor}; -`; - -const Img = styled.img` - width: 35px; - height: 35px; - margin-right: 10px; -`; - -const CoinsList = styled.ul``; - -const Coin = styled.li` - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => props.theme.bgColor}; - margin-bottom: 10px; - border-radius: 15px; - a { - padding: 20px; - display: flex; - align-items: center; - transition: color 0.2s ease-in-out; - } - &:hover { - a { - color: ${(props) => props.theme.accentColor}; - } - } -`; - -interface ICoin { - id: string; - name: string; - symbol: string; - rank: number; - is_new: boolean; - is_active: boolean; - type: string; -} - -function Coins() { - /* - const [loading, setLoading] = useState(true); - const [coins, setCoins] = useState([]); - useEffect(() => { - (async () => { - const response = await fetch("https://api.coinpaprika.com/v1/coins"); - const json = await response.json(); - setCoins(json.slice(0, 100)); - //console.log(json); - setLoading(false); - })(); - }, []);*/ - const { isLoading, data } = useQuery("allcoins", fetchCoins); - - return ( - - - 코인 - -
- 코인 -
- {isLoading ? ( - Loading... - ) : ( - - {data?.slice(0, 100).map((item) => ( - - - - {item.name} → - - - ))} - - )} -
- ); -} - -export default Coins; diff --git a/src/Routes/Home.tsx b/src/Routes/Home.tsx new file mode 100644 index 0000000..c7f4b70 --- /dev/null +++ b/src/Routes/Home.tsx @@ -0,0 +1,191 @@ +import { useQuery } from "react-query"; +import { useLocation } from "react-router-dom"; +import styled from "styled-components"; +import { + getMoviesPage1, + getMoviesPage2, + getMoviesPage3, + getMoviesPage4, + getMoviesPage5, + getMoviesPage6, + getUpcommingData, + IGetVideosProps, + IMovie, + topRateMoviePage1, + topRateMoviePage2, + topRateMoviePage3, + topRateMoviePage4, + topRateMoviePage5, + topRateMoviePage6, +} from "../api"; +import BannerScreen from "../Components/BannerScreen"; +import Footer from "../Components/Footer"; +import NowMovies from "../Components/NowMovies"; + +const Wrapper = styled.div``; + +const Loader = styled.div``; + +function Home() { + const nowMovieData: IMovie[] = []; + const recommendData: IMovie[] = []; + const topRateMovies: IMovie[] = []; + const recommendData2: IMovie[] = []; + const upcommingData: IMovie[] = []; + const nowPage1 = useQuery( + ["movies", "nowPlayingPage1"], + getMoviesPage1 + ); + const nowPage2 = useQuery( + ["movies", "nowPlayingPage2"], + getMoviesPage2 + ); + const nowPage3 = useQuery( + ["movies", "nowPlayingPage3"], + getMoviesPage3 + ); + const nowPage4 = useQuery( + ["movies", "nowPlayingPage4"], + getMoviesPage4 + ); + const nowPage5 = useQuery( + ["movies", "nowPlayingPage5"], + getMoviesPage5 + ); + const nowPage6 = useQuery( + ["movies", "nowPlayingPage6"], + getMoviesPage6 + ); + + const topRatePage1 = useQuery( + ["movies", "topRatePage1"], + topRateMoviePage1 + ); + + const topRatePage2 = useQuery( + ["movies", "topRatePage2"], + topRateMoviePage2 + ); + + const topRatePage3 = useQuery( + ["movies", "topRatePage3"], + topRateMoviePage3 + ); + + const topRatePage4 = useQuery( + ["movies", "topRatePage4"], + topRateMoviePage4 + ); + + const topRatePage5 = useQuery( + ["movies", "topRatePage5"], + topRateMoviePage5 + ); + + const topRatePage6 = useQuery( + ["movies", "topRatePage6"], + topRateMoviePage6 + ); + + const upcommingPage1 = useQuery( + ["movies", "upcomming1"], + () => getUpcommingData(1) + ); + const upcommingPage2 = useQuery( + ["movies", "upcomming2"], + () => getUpcommingData(2) + ); + const upcommingPage3 = useQuery( + ["movies", "upcomming3"], + () => getUpcommingData(3) + ); + + //console.log(nowPage1); + nowPage1?.data?.results.map((item) => nowMovieData.push(item)); + nowPage2?.data?.results.map((item) => nowMovieData.push(item)); + nowPage3?.data?.results.map((item) => nowMovieData.push(item)); + + nowPage4?.data?.results.map((item) => recommendData.push(item)); + nowPage5?.data?.results.map((item) => recommendData.push(item)); + nowPage6?.data?.results.map((item) => recommendData.push(item)); + + topRatePage1?.data?.results.map((item) => topRateMovies.push(item)); + topRatePage2?.data?.results.map((item) => topRateMovies.push(item)); + topRatePage3?.data?.results.map((item) => topRateMovies.push(item)); + + topRatePage4?.data?.results.map((item) => recommendData2.push(item)); + topRatePage5?.data?.results.map((item) => recommendData2.push(item)); + topRatePage6?.data?.results.map((item) => recommendData2.push(item)); + + upcommingPage1.data?.results.map((item) => upcommingData.push(item)); + upcommingPage2.data?.results.map((item) => upcommingData.push(item)); + upcommingPage3.data?.results.map((item) => upcommingData.push(item)); + // console.log(clickedMovie); + //console.log(index, "인뎃으"); + //console.log(nowMovieData, isLoading); + + const location = useLocation(); + + return ( + + {upcommingData.length < 58 ? ( + Loading... + ) : ( + <> + {nowPage1.data?.results[0] && ( + + )} + + {nowMovieData && ( + + )} + {topRateMovies && ( + + )} + {recommendData && ( + + )} + {recommendData2 && ( + + )} + {upcommingData && ( + + )} +