From cc10f670ca8657d47f5bcee02f8abb587c6875e1 Mon Sep 17 00:00:00 2001 From: TaegeonKim <61930500+Taegon21@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:32:09 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Integrate=20login=20with=20Cognito?= =?UTF-8?q?=20and=20set=20up=20API=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Implement login with AWS Cognito * ✨ Manage idToken with cookie * ✨ Integrate API logic with React Query * :sparkles: Create API integration test page --- package-lock.json | 515 +++++++++++++++++- package.json | 11 +- src/api/auth/auth.ts | 147 +++++ src/api/client.ts | 28 + src/api/endpoints.ts | 12 + src/api/hooks/useProfessor.ts | 44 ++ src/api/hooks/useStudent.ts | 41 ++ src/app/(main)/layout.tsx | 11 +- src/app/(main)/test/page.tsx | 57 ++ src/app/(main)/test2/page.tsx | 88 +++ src/app/(onboarding)/login/page.module.css | 25 +- src/app/(onboarding)/login/page.tsx | 190 +++++-- src/app/(onboarding)/signup/page.tsx | 95 +++- .../(onboarding)/verify-email/page.module.css | 147 +++++ src/app/(onboarding)/verify-email/page.tsx | 92 ++++ src/app/(professor)/layout.tsx | 10 +- src/app/layout.tsx | 4 +- src/store/userStore.tsx | 66 +++ src/utils/provider.tsx | 19 + 19 files changed, 1510 insertions(+), 92 deletions(-) create mode 100644 src/api/auth/auth.ts create mode 100644 src/api/client.ts create mode 100644 src/api/endpoints.ts create mode 100644 src/api/hooks/useProfessor.ts create mode 100644 src/api/hooks/useStudent.ts create mode 100644 src/app/(main)/test/page.tsx create mode 100644 src/app/(main)/test2/page.tsx create mode 100644 src/app/(onboarding)/verify-email/page.module.css create mode 100644 src/app/(onboarding)/verify-email/page.tsx create mode 100644 src/store/userStore.tsx create mode 100644 src/utils/provider.tsx diff --git a/package-lock.json b/package-lock.json index b2a8150..389ad6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,24 @@ "name": "devroom-client", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.48.0", + "@tanstack/react-query-devtools": "^5.48.0", + "@tanstack/react-query-next-experimental": "^5.48.0", + "amazon-cognito-identity-js": "^6.3.12", + "axios": "^1.7.2", + "js-cookie": "^3.0.5", "next": "14.2.3", + "next-auth": "^4.24.7", "react": "^18", "react-dom": "^18", "zustand": "^4.5.2" }, "devDependencies": { "@svgr/webpack": "^8.1.0", + "@types/js-cookie": "^3.0.6", + "@types/next": "^9.0.0", "@types/node": "^20", - "@types/react": "^18", + "@types/react": "^18.3.3", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.3", @@ -36,6 +45,56 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/sha256-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz", + "integrity": "sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==", + "dependencies": { + "@aws-crypto/util": "^1.2.2", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz", + "integrity": "sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==", + "dependencies": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -1971,7 +2030,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2402,6 +2460,14 @@ "node": ">= 8" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.0.tgz", + "integrity": "sha512-97ZQvZJ4gJhi24Io6zI+W7B67I82q1I8i3BSzQ4OyZj1z4OW87/ruF26lrMES58inTKLy2KgVIDcx8PU4AaANQ==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2418,6 +2484,17 @@ "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "dev": true }, + "node_modules/@smithy/types": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.2.0.tgz", + "integrity": "sha512-cKyeKAPazZRVqm7QPvcPD2jEIt2wqDPAL1KJKb0f/5I7uhollvsWZuZKLclmyP6a+Jwmr3OV3t+X0pZUUHS9BA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -2689,6 +2766,69 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.48.0.tgz", + "integrity": "sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.47.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.47.0.tgz", + "integrity": "sha512-oo10s7Nqaf/Q3QF4vuSwS0xw7zuYr5nXef4RHQIXVJKvRQeo9WowPLAnB0SF/voB0c4GTX6Vq8wfQ1G72ezEdw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.48.0.tgz", + "integrity": "sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw==", + "dependencies": { + "@tanstack/query-core": "5.48.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.48.0.tgz", + "integrity": "sha512-0xvk8KDvEfQuLz3dy9jqtP0f6iR724d57Br7KtrtClbyqB53cy3Iy6BiTsiHaSsYnex/+cxWJZIX1UJWeV8Amw==", + "dependencies": { + "@tanstack/query-devtools": "5.47.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.48.0", + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-next-experimental": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-next-experimental/-/react-query-next-experimental-5.48.0.tgz", + "integrity": "sha512-+t41XBiPMtwa+xEfsNuVCxPCQQh4vnZ4Xzd6NtDaqWMsWSeR6hN6mTtMvJb2KFzBs8gZghB8yebLRE4msYnF1A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.48.0", + "next": "^13 || ^14 || ^15", + "react": "^18 || ^19" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -2698,12 +2838,28 @@ "node": ">=10.13.0" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/next": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/next/-/next-9.0.0.tgz", + "integrity": "sha512-gnBXM8rP1mnCgT1uE2z8SnpFTKRWReJlhbZLZkOLq/CH1ifvTNwjIVtXvsywTy1dwVklf+y/MB0Eh6FOa94yrg==", + "deprecated": "This is a stub types definition. next provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "next": "*" + } + }, "node_modules/@types/node": { "version": "20.14.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", @@ -2908,6 +3064,23 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/amazon-cognito-identity-js": { + "version": "6.3.12", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.12.tgz", + "integrity": "sha512-s7NKDZgx336cp+oDeUtB2ZzT8jWJp/v2LWuYl+LQtMEODe22RF1IJ4nRiDATp+rp1pTffCZcm44Quw4jx2bqNg==", + "dependencies": { + "@aws-crypto/sha256-js": "1.2.2", + "buffer": "4.9.2", + "fast-base64-decode": "^1.0.0", + "isomorphic-unfetch": "^3.0.0", + "js-cookie": "^2.2.1" + } + }, + "node_modules/amazon-cognito-identity-js/node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3124,6 +3297,11 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "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/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3148,6 +3326,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -3211,6 +3399,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3271,6 +3478,21 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3380,6 +3602,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "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/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3401,6 +3634,14 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-compat": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", @@ -3657,6 +3898,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4398,6 +4647,11 @@ "node": ">=0.10.0" } }, + "node_modules/fast-base64-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", + "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4513,6 +4767,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4538,6 +4811,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4850,6 +5136,25 @@ "node": ">= 0.4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -5288,6 +5593,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -5319,6 +5633,22 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jose": { + "version": "4.15.7", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.7.tgz", + "integrity": "sha512-L7ioP+JAuZe8v+T5+zVI9Tx8LtU8BL7NxkyDFVMv+Qr3JW0jSoYDedLtodaXwfqMpeCyx4WXFNyu9tJt4WvC1A==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5529,6 +5859,25 @@ "node": ">=8.6" } }, + "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/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5637,6 +5986,33 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.7", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz", + "integrity": "sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13 || ^14", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -5647,6 +6023,25 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -5665,6 +6060,11 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5674,6 +6074,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -5790,6 +6198,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5799,6 +6215,36 @@ "wrappy": "1" } }, + "node_modules/openid-client": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", + "integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==", + "dependencies": { + "jose": "^4.15.5", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5987,6 +6433,26 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5996,6 +6462,11 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -6007,6 +6478,11 @@ "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/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6107,8 +6583,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -6752,6 +7227,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -6912,6 +7392,11 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -6999,6 +7484,28 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 8edb6ba..ceaa846 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,24 @@ "lint": "next lint" }, "dependencies": { + "@tanstack/react-query": "^5.48.0", + "@tanstack/react-query-devtools": "^5.48.0", + "@tanstack/react-query-next-experimental": "^5.48.0", + "amazon-cognito-identity-js": "^6.3.12", + "axios": "^1.7.2", + "js-cookie": "^3.0.5", "next": "14.2.3", + "next-auth": "^4.24.7", "react": "^18", "react-dom": "^18", "zustand": "^4.5.2" }, "devDependencies": { "@svgr/webpack": "^8.1.0", + "@types/js-cookie": "^3.0.6", + "@types/next": "^9.0.0", "@types/node": "^20", - "@types/react": "^18", + "@types/react": "^18.3.3", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.3", diff --git a/src/api/auth/auth.ts b/src/api/auth/auth.ts new file mode 100644 index 0000000..be92ce9 --- /dev/null +++ b/src/api/auth/auth.ts @@ -0,0 +1,147 @@ +import { + CognitoUserPool, + CognitoUserAttribute, + ISignUpResult, + AuthenticationDetails, + CognitoUser, +} from "amazon-cognito-identity-js"; + +interface SignUpParams { + email: string; + password: string; + name: string; // custom:name 속성 + role: string; // custom:role 속성 + studentId: string; // custom:student_id 속성 + onSuccess: (result: ISignUpResult) => void; + onFailure: (error: Error) => void; +} + +interface AuthResponse { + idToken: string; + name: string; // 사용자의 이름 + role: string; // 사용자의 역할 + studentId: string; // 학생 ID +} + +interface VerifyEmailParams { + username: string; // 이메일 주소 + code: string; // 사용자로부터 받은 인증 코드 +} + +const USER_POOL_ID = process.env.NEXT_PUBLIC_COGNITO_USER_POOL_ID; +const CLIENT_ID = process.env.NEXT_PUBLIC_COGNITO_CLIENT_ID; + +if (!USER_POOL_ID || !CLIENT_ID) { + throw new Error( + "Cognito credentials are not properly set in the environment variables." + ); +} + +const poolData = { + UserPoolId: USER_POOL_ID, + ClientId: CLIENT_ID, +}; + +const userPool = new CognitoUserPool(poolData); + +export function signUp({ + email, + password, + name, + role, + studentId, + onSuccess, + onFailure, +}: SignUpParams): void { + const username = email; + const attributeList = [ + new CognitoUserAttribute({ + Name: "email", + Value: email, + }), + new CognitoUserAttribute({ + Name: "custom:name", + Value: name, + }), + new CognitoUserAttribute({ + Name: "custom:role", + Value: role, + }), + new CognitoUserAttribute({ + Name: "custom:student_id", + Value: studentId, + }), + ]; + + userPool.signUp(username, password, attributeList, [], (err, result) => { + if (err) { + onFailure(err); + return; + } + if (result) { + onSuccess(result); + } + }); +} + +export function authenticateCognitoUser( + email: string, + password: string +): Promise { + const authenticationDetails = new AuthenticationDetails({ + Username: email, + Password: password, + }); + + const userData = { + Username: email, + Pool: userPool, + }; + + const cognitoUser = new CognitoUser(userData); + return new Promise((resolve, reject) => { + cognitoUser.authenticateUser(authenticationDetails, { + onSuccess: (result) => { + const idToken = result.getIdToken().getJwtToken(); + const claims = result.getIdToken().decodePayload(); + + // custom 속성 가져오기 + const name = claims["custom:name"] || ""; + const role = claims["custom:role"] || ""; + const studentId = claims["custom:student_id"] || ""; + + resolve({ + idToken, + name, + role, + studentId, + }); + }, + onFailure: (err) => { + reject(err); + }, + }); + }); +} + +export function verifyEmail({ + username, + code, +}: VerifyEmailParams): Promise { + const userData = { + Username: username, + Pool: userPool, + }; + + const cognitoUser = new CognitoUser(userData); + + return new Promise((resolve, reject) => { + cognitoUser.confirmRegistration(code, true, (err, result) => { + if (err) { + reject(err); + return; + } + resolve(); + }); + }); +} diff --git a/src/api/client.ts b/src/api/client.ts new file mode 100644 index 0000000..1495e97 --- /dev/null +++ b/src/api/client.ts @@ -0,0 +1,28 @@ +import axios from "axios"; +import Cookies from "js-cookie"; + +const apiUrl = process.env.NEXT_PUBLIC_API_URL; + +const apiClient = axios.create({ + baseURL: apiUrl, + headers: { + "Content-Type": "application/json", + }, +}); + +apiClient.interceptors.request.use( + async (config) => { + const token = Cookies.get("idToken"); + console.log("🚀 ~ file: client.ts:16 ~ token:", token); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + console.log("error", error); + return Promise.reject(error); + } +); + +export default apiClient; diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts new file mode 100644 index 0000000..f1dc917 --- /dev/null +++ b/src/api/endpoints.ts @@ -0,0 +1,12 @@ +export const API_ENDPOINTS = { + STUDENT: { + SERVICE: (studentId: string) => `/service/${studentId}`, + POD: (studentId: string) => `/pod/${studentId}`, + DEPLOY: (studentId: string) => `/deploy/${studentId}`, + }, + PROFESSOR: { + CHECK: (professorId: string) => `/class/${professorId}/pod`, + CREATE: (professorId: string) => `/class/${professorId}/create`, + DELETE: (professorId: string) => `/class/${professorId}/delete`, + }, +}; diff --git a/src/api/hooks/useProfessor.ts b/src/api/hooks/useProfessor.ts new file mode 100644 index 0000000..5f61ef1 --- /dev/null +++ b/src/api/hooks/useProfessor.ts @@ -0,0 +1,44 @@ +import apiClient from "@/api/client"; +import { API_ENDPOINTS } from "@/api/endpoints"; +import { useQuery, useMutation } from "@tanstack/react-query"; + +const fetchCheck = async (professorId: string) => { + const { data } = await apiClient.get( + API_ENDPOINTS.PROFESSOR.CHECK(professorId) + ); + return data; +}; + +export const useFetchCheck = (professorId: string) => { + return useQuery({ + queryKey: ["fetchDeploy", professorId], + queryFn: () => fetchCheck(professorId), + }); +}; + +interface ClassCreationData { + className: string; + studentIds: string[]; + options: { [key: string]: string }; + command: string[]; + customScript: string; +} + +interface CreateClassArgs { + professorId: string; + classData: ClassCreationData; +} + +const createClass = async ({ professorId, classData }: CreateClassArgs) => { + const { data } = await apiClient.post( + API_ENDPOINTS.PROFESSOR.CREATE(professorId), + classData + ); + return data; +}; + +export const useCreateClass = () => { + return useMutation({ + mutationFn: createClass, + }); +}; diff --git a/src/api/hooks/useStudent.ts b/src/api/hooks/useStudent.ts new file mode 100644 index 0000000..7b0ddeb --- /dev/null +++ b/src/api/hooks/useStudent.ts @@ -0,0 +1,41 @@ +import apiClient from "@/api/client"; +import { API_ENDPOINTS } from "@/api/endpoints"; +import { useQuery } from "@tanstack/react-query"; + +const fetchService = async (studentId: string) => { + const { data } = await apiClient.get( + API_ENDPOINTS.STUDENT.SERVICE(studentId) + ); + return data; +}; + +const fetchPod = async (studentId: string) => { + const { data } = await apiClient.get(API_ENDPOINTS.STUDENT.POD(studentId)); + return data; +}; + +const fetchDeploy = async (studentId: string) => { + const { data } = await apiClient.get(API_ENDPOINTS.STUDENT.DEPLOY(studentId)); + return data; +}; + +export const useFetchService = (studentId: string) => { + return useQuery({ + queryKey: ["fetchService", studentId], + queryFn: () => fetchService(studentId), + }); +}; + +export const useFetchPod = (studentId: string) => { + return useQuery({ + queryKey: ["fetchPod", studentId], + queryFn: () => fetchPod(studentId), + }); +}; + +export const useFetchDeploy = (studentId: string) => { + return useQuery({ + queryKey: ["fetchDeploy", studentId], + queryFn: () => fetchDeploy(studentId), + }); +}; diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 28a2670..b38d373 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -4,7 +4,7 @@ import { ReactNode, useEffect, useState } from "react"; import styles from "./layout.module.css"; import { useAuthStore } from "@/store/authStore"; import Link from "next/link"; -import { redirect, usePathname } from "next/navigation"; +import { usePathname } from "next/navigation"; import Image from "next/image"; import LeftArrowIcon from "/public/icons/LeftArrow.svg"; import HamburgerIcon from "/public/icons/Hamburger.svg"; @@ -15,15 +15,8 @@ interface LayoutProps { export default function Layout({ children }: LayoutProps) { const user = useAuthStore((state) => state.user); - const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const isLoading = useAuthStore((state) => state.isLoading); - const [isSidebarOpen, setSidebarOpen] = useState(true); - useEffect(() => { - if (!isLoggedIn && !isLoading) { - redirect("/onboarding"); - } - }, [isLoggedIn, isLoading]); + const [isSidebarOpen, setSidebarOpen] = useState(true); const navigation = usePathname(); diff --git a/src/app/(main)/test/page.tsx b/src/app/(main)/test/page.tsx new file mode 100644 index 0000000..e6a12e2 --- /dev/null +++ b/src/app/(main)/test/page.tsx @@ -0,0 +1,57 @@ +"use client"; + +import React from "react"; +import { + useFetchService, + useFetchPod, + useFetchDeploy, +} from "@/api/hooks/useStudent"; + +const StudentInfoComponent = () => { + const studentId = "2019312430"; + const { + data: serviceData, + isLoading: isLoadingService, + error: errorService, + } = useFetchService(studentId); + const { + data: podData, + isLoading: isLoadingPod, + error: errorPod, + } = useFetchPod(studentId); + const { + data: deployData, + isLoading: isLoadingDeploy, + error: errorDeploy, + } = useFetchDeploy(studentId); + + // 로딩 상태 처리 + if (isLoadingService || isLoadingPod || isLoadingDeploy) { + return
Loading...
; + } + + // 에러 상태 처리 + if (errorService || errorPod || errorDeploy) { + return
Error loading data. Please try again later.
; + } + + return ( +
+

Student Information

+
+

Service Details

+

{JSON.stringify(serviceData, null, 2)}

+
+
+

Pod Details

+

{JSON.stringify(podData, null, 2)}

+
+
+

Deployment Details

+

{JSON.stringify(deployData, null, 2)}

+
+
+ ); +}; + +export default StudentInfoComponent; diff --git a/src/app/(main)/test2/page.tsx b/src/app/(main)/test2/page.tsx new file mode 100644 index 0000000..30e0821 --- /dev/null +++ b/src/app/(main)/test2/page.tsx @@ -0,0 +1,88 @@ +"use client"; +import React, { useState, ChangeEvent, FormEvent } from "react"; +import { useCreateClass } from "@/api/hooks/useProfessor"; + +interface ClassCreationData { + className: string; + studentIds: string[]; + options: { [key: string]: string }; + command: string[]; + customScript: string; +} + +const CreateClassForm: React.FC = () => { + const [professorId, setProfessorId] = useState("2019312218"); + const [classData, setClassData] = useState({ + className: "", + studentIds: [], + options: { vscode: "yes" }, + command: [], + customScript: "", + }); + + const { mutate: createClass, isError, error } = useCreateClass(); + + const handleChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setClassData((prev) => ({ ...prev, [name]: value })); + }; + + const handleStudentIdsChange = (e: ChangeEvent) => { + const { value } = e.target; + setClassData((prev) => ({ + ...prev, + studentIds: value.split(",").map((id) => id.trim()), + })); + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + createClass({ professorId, classData }); + }; + + return ( +
+

Create a New Class

+ + + +