From b37a0891e6b1dc10c69db7c2d3efaabe54b5f23d Mon Sep 17 00:00:00 2001 From: Muhtalip Dede Date: Thu, 18 May 2023 14:21:57 +0300 Subject: [PATCH 1/7] add redux for state managament --- rating-system/package-lock.json | 250 ++++++++++++++++++++++++++++ rating-system/package.json | 2 + rating-system/pages/_app.tsx | 7 +- rating-system/pages/index.tsx | 66 +++++--- rating-system/prisma/schema.prisma | 19 +++ rating-system/store/actions/user.ts | 21 +++ rating-system/store/hooks.ts | 5 + rating-system/store/index.ts | 16 ++ rating-system/store/reducer/user.ts | 30 ++++ 9 files changed, 388 insertions(+), 28 deletions(-) create mode 100644 rating-system/store/actions/user.ts create mode 100644 rating-system/store/hooks.ts create mode 100644 rating-system/store/index.ts create mode 100644 rating-system/store/reducer/user.ts diff --git a/rating-system/package-lock.json b/rating-system/package-lock.json index 3a28c74..111ca4f 100644 --- a/rating-system/package-lock.json +++ b/rating-system/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "^4.14.0", + "@reduxjs/toolkit": "^1.9.5", "@types/node": "20.1.4", "@types/react": "18.2.6", "@types/react-dom": "18.2.4", @@ -18,6 +19,7 @@ "postcss": "8.4.23", "react": "18.2.0", "react-dom": "18.2.0", + "react-redux": "^8.0.5", "tailwindcss": "3.3.2", "typescript": "5.0.4" }, @@ -36,6 +38,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/runtime": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", + "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -315,6 +328,29 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c.tgz", "integrity": "sha512-3jum8/YSudeSN0zGW5qkpz+wAN2V/NYCQ+BPjvHYDfWatLWlQkqy99toX0GysDeaUoBIJg1vaz2yKqiA3CFcQw==" }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -323,6 +359,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/node": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", @@ -356,6 +401,11 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -879,6 +929,19 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -891,6 +954,15 @@ "node": ">= 6" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1535,6 +1607,49 @@ "react": "^18.2.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -1567,6 +1682,32 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -1920,6 +2061,14 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1980,6 +2129,14 @@ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" }, + "@babel/runtime": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", + "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -2143,6 +2300,17 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c.tgz", "integrity": "sha512-3jum8/YSudeSN0zGW5qkpz+wAN2V/NYCQ+BPjvHYDfWatLWlQkqy99toX0GysDeaUoBIJg1vaz2yKqiA3CFcQw==" }, + "@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "requires": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + } + }, "@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -2151,6 +2319,15 @@ "tslib": "^2.4.0" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/node": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", @@ -2184,6 +2361,11 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2549,6 +2731,21 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -2558,6 +2755,11 @@ "debug": "4" } }, + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2953,6 +3155,24 @@ "scheduler": "^0.23.0" } }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -2979,6 +3199,30 @@ "picomatch": "^2.2.1" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "requires": {} + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -3200,6 +3444,12 @@ "picocolors": "^1.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/rating-system/package.json b/rating-system/package.json index 3d618a4..2f6d5a4 100644 --- a/rating-system/package.json +++ b/rating-system/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@prisma/client": "^4.14.0", + "@reduxjs/toolkit": "^1.9.5", "@types/node": "20.1.4", "@types/react": "18.2.6", "@types/react-dom": "18.2.4", @@ -19,6 +20,7 @@ "postcss": "8.4.23", "react": "18.2.0", "react-dom": "18.2.0", + "react-redux": "^8.0.5", "tailwindcss": "3.3.2", "typescript": "5.0.4" }, diff --git a/rating-system/pages/_app.tsx b/rating-system/pages/_app.tsx index 487021b..2d22f7e 100644 --- a/rating-system/pages/_app.tsx +++ b/rating-system/pages/_app.tsx @@ -1,6 +1,9 @@ import type { AppProps } from 'next/app' -import { useEffect } from 'react'; +import { Provider } from 'react-redux'; +import store from '../store'; export default function App({ Component, pageProps }: AppProps) { - return + return + + } \ No newline at end of file diff --git a/rating-system/pages/index.tsx b/rating-system/pages/index.tsx index 481cddb..12dfe56 100644 --- a/rating-system/pages/index.tsx +++ b/rating-system/pages/index.tsx @@ -1,35 +1,49 @@ -import React, { useEffect, useState } from 'react' -import Input from '../components/Input' -import Button from '../components/Button' - -type UserType = { - id: String - name: String - email: String - password: String - createdAt: String - updatedAt: String -} - -type Users = UserType[] +import React, { useEffect } from 'react' +import { useAppDispatch, useAppSelector } from '../store/hooks' +import { RootState } from '../store' +import { setUser } from '../store/actions/user' const Index = () => { - const [users, setUsers] = useState([]); - const getAllUsers = async () => { - const users = await fetch('/api/user') - setUsers(await users.json()) + const dispatch = useAppDispatch(); + const user = useAppSelector((state: RootState) => state.user.user); + + const [name, setName] = React.useState('') + const [email, setEmail] = React.useState('') + const [password, setPassword] = React.useState('') + + const handleNameChange = (e: React.ChangeEvent) => { + setName(e.target.value) + } + + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value) + } + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value) + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + dispatch(setUser(name, email, password)) } - useEffect(()=>{ - getAllUsers() - },[]) return (
- {users.map((user,index)=> ( -
{user.name}
- ))} - -
) } diff --git a/rating-system/prisma/schema.prisma b/rating-system/prisma/schema.prisma index 2eb19ab..29b981a 100644 --- a/rating-system/prisma/schema.prisma +++ b/rating-system/prisma/schema.prisma @@ -45,6 +45,7 @@ model Session { criterias CriteriaOnSession[] candidates CandidateOnSession[] participants ParticipantOnSession[] + Rating Rating? } model Criteria { @@ -55,6 +56,7 @@ model Criteria { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions CriteriaOnSession[] + Rating Rating? } model CriteriaOnSession { @@ -82,6 +84,7 @@ model Candidate { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions CandidateOnSession[] + Rating Rating? } model CandidateOnSession { @@ -101,6 +104,7 @@ model Participant { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions ParticipantOnSession[] + Rating Rating? } model ParticipantOnSession { @@ -112,3 +116,18 @@ model ParticipantOnSession { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + +model Rating { + id String @id @default(auto()) @map("_id") @db.ObjectId + score Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + candidate Candidate @relation(fields: [candidate_id], references: [id]) + candidate_id String @unique @db.ObjectId + participant Participant @relation(fields: [participant_id], references: [id]) + participant_id String @unique @db.ObjectId + criteria Criteria @relation(fields: [criteria_id], references: [id]) + criteria_id String @unique @db.ObjectId + session Session @relation(fields: [session_id], references: [id]) + session_id String @unique @db.ObjectId +} diff --git a/rating-system/store/actions/user.ts b/rating-system/store/actions/user.ts new file mode 100644 index 0000000..9f9d552 --- /dev/null +++ b/rating-system/store/actions/user.ts @@ -0,0 +1,21 @@ +import { User } from "@prisma/client"; +import { AppThunk } from ".."; + +export enum UserActionTypes { + SET_USER = "SET_USER", +} + +export const setUser = (name: string, email: string, password: string): AppThunk> => { + return async (dispatch, getState) => { + const state = getState(); + console.log(state.user.user); + dispatch({ + type: UserActionTypes.SET_USER, + payload: { + name, + email, + password, + }, + }); + }; +} \ No newline at end of file diff --git a/rating-system/store/hooks.ts b/rating-system/store/hooks.ts new file mode 100644 index 0000000..a72787d --- /dev/null +++ b/rating-system/store/hooks.ts @@ -0,0 +1,5 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "."; + +export const useAppDispatch = () => useDispatch(); +export const useAppSelector: TypedUseSelectorHook = useSelector; \ No newline at end of file diff --git a/rating-system/store/index.ts b/rating-system/store/index.ts new file mode 100644 index 0000000..54501c3 --- /dev/null +++ b/rating-system/store/index.ts @@ -0,0 +1,16 @@ +import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit"; +import userReducer from "./reducer/user"; + +const store = configureStore({ + reducer: { + user: userReducer, + } +}) + +export type RootState = ReturnType + +export type AppDispatch = typeof store.dispatch; + +export type AppThunk = ThunkAction> + +export default store \ No newline at end of file diff --git a/rating-system/store/reducer/user.ts b/rating-system/store/reducer/user.ts new file mode 100644 index 0000000..e4e2df2 --- /dev/null +++ b/rating-system/store/reducer/user.ts @@ -0,0 +1,30 @@ +import { User } from "@prisma/client"; +import { UserActionTypes } from "../actions/user"; +import { AnyAction, Reducer } from "@reduxjs/toolkit"; + +type UserState = { + user: User | null; +}; + +const initialState: UserState = { + user: null, +}; + +export interface UserAction { + type: UserActionTypes; + payload: User; +} + +const userReducer: Reducer = (state = initialState, action) => { + switch (action.type) { + case UserActionTypes.SET_USER: + return { + ...state, + user: action.payload, + }; + default: + return state; + } +}; + +export default userReducer; \ No newline at end of file From 3bf5dc900d451c7306730334f76e988ce169fd2d Mon Sep 17 00:00:00 2001 From: Muhtalip Dede Date: Sun, 21 May 2023 13:14:07 +0300 Subject: [PATCH 2/7] =?UTF-8?q?1.=20yay=C4=B1n=20geli=C5=9Ftirmeleri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating-system/fetch-helpers/post.ts | 13 +++++++++ rating-system/pages/api/login.ts | 14 ++++++++++ rating-system/pages/api/user.ts | 5 ++-- rating-system/pages/home/index.tsx | 9 +++++++ rating-system/pages/login.tsx | 9 ------- rating-system/pages/login/index.tsx | 39 +++++++++++++++++++++++++++ rating-system/pages/signup/index.tsx | 40 +++++++++++++++++++++++++--- rating-system/prisma/schema.prisma | 16 +++++------ rating-system/prisma/user.ts | 15 ++++++++++- 9 files changed, 137 insertions(+), 23 deletions(-) create mode 100644 rating-system/fetch-helpers/post.ts create mode 100644 rating-system/pages/api/login.ts create mode 100644 rating-system/pages/home/index.tsx delete mode 100644 rating-system/pages/login.tsx create mode 100644 rating-system/pages/login/index.tsx diff --git a/rating-system/fetch-helpers/post.ts b/rating-system/fetch-helpers/post.ts new file mode 100644 index 0000000..02c5279 --- /dev/null +++ b/rating-system/fetch-helpers/post.ts @@ -0,0 +1,13 @@ +export const fetchPost = async (path: string, data: T) => { + const result = await fetch(path, { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!result.ok) throw new Error(result.statusText || 'Something went wrong'); + + return await result.json() as T; +}; \ No newline at end of file diff --git a/rating-system/pages/api/login.ts b/rating-system/pages/api/login.ts new file mode 100644 index 0000000..83537bc --- /dev/null +++ b/rating-system/pages/api/login.ts @@ -0,0 +1,14 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { login } from "../../prisma/user"; + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + switch (req.method) { + case 'POST': { + const { email, password } = req.body; + const user = await login(email, password); + return res.json(user); + } + default: + return res.status(405).end(`Method ${req.method} Not Allowed`); + } +} \ No newline at end of file diff --git a/rating-system/pages/api/user.ts b/rating-system/pages/api/user.ts index ea71a1d..c1af95e 100644 --- a/rating-system/pages/api/user.ts +++ b/rating-system/pages/api/user.ts @@ -20,8 +20,9 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) } } case 'POST': { - const { email, name, birthYear } = req.body - const user = await createUser(email, name, birthYear) + const { email, name, password } = req.body + console.log(email, name, password); + const user = await createUser(email, name, password) return res.json(user) } case 'PUT': { diff --git a/rating-system/pages/home/index.tsx b/rating-system/pages/home/index.tsx new file mode 100644 index 0000000..a98c99e --- /dev/null +++ b/rating-system/pages/home/index.tsx @@ -0,0 +1,9 @@ +const Home = () => { + return ( + <> +

Home

+ + ) +} + +export default Home \ No newline at end of file diff --git a/rating-system/pages/login.tsx b/rating-system/pages/login.tsx deleted file mode 100644 index 45e3e9d..0000000 --- a/rating-system/pages/login.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const Login = () => { - return ( -
Login
- ) -} - -export default Login \ No newline at end of file diff --git a/rating-system/pages/login/index.tsx b/rating-system/pages/login/index.tsx new file mode 100644 index 0000000..cef1cca --- /dev/null +++ b/rating-system/pages/login/index.tsx @@ -0,0 +1,39 @@ +import { User } from "@prisma/client" +import { useRouter } from "next/router" +import { useState } from "react" +import { fetchPost } from "../../fetch-helpers/post" + +const Login = () => { + const router = useRouter() + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value) + } + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value) + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + const result = await fetchPost('/api/login', { email, password } as User) + if (result && result.id) { + router.push('/home') + } + } + + return ( + <> +

Login

+
+ + + +
+ + ) +} + +export default Login \ No newline at end of file diff --git a/rating-system/pages/signup/index.tsx b/rating-system/pages/signup/index.tsx index 62354d1..8f7e6e9 100644 --- a/rating-system/pages/signup/index.tsx +++ b/rating-system/pages/signup/index.tsx @@ -1,9 +1,43 @@ +import { User } from '@prisma/client' +import { useRouter } from 'next/router' import React from 'react' +import { fetchPost } from '../../fetch-helpers/post' + +const Signup = () => { + const router = useRouter() + const [name, setName] = React.useState('') + const [email, setEmail] = React.useState('') + const [password, setPassword] = React.useState('') + + const handleNameChange = (e: React.ChangeEvent) => { + setName(e.target.value) + } + + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value) + } + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value) + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + const result = await fetchPost('/api/user', { name, email, password } as User) + if (result && result.id) { + router.push('/login') + } + } -const SignupById = () => { return ( -
Signup
+
+

Rating System

+ + + + +
) } -export default SignupById \ No newline at end of file +export default Signup \ No newline at end of file diff --git a/rating-system/prisma/schema.prisma b/rating-system/prisma/schema.prisma index 29b981a..a5b7c0e 100644 --- a/rating-system/prisma/schema.prisma +++ b/rating-system/prisma/schema.prisma @@ -10,19 +10,19 @@ datasource db { model User { id String @id @default(auto()) @map("_id") @db.ObjectId name String - email String + email String @unique password String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user_type UserType? - Candidate Candidate? - Participant Participant? - Admin Admin? + candidate Candidate? + participant Participant? + admin Admin? } model UserType { id String @id @default(auto()) @map("_id") @db.ObjectId - type UserTypes @default(PARTICIPANT) + type UserTypes @default(CANDIDATE) user User @relation(fields: [user_id], references: [id]) user_id String @unique @db.ObjectId createdAt DateTime @default(now()) @@ -45,7 +45,7 @@ model Session { criterias CriteriaOnSession[] candidates CandidateOnSession[] participants ParticipantOnSession[] - Rating Rating? + rating Rating? } model Criteria { @@ -56,7 +56,7 @@ model Criteria { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions CriteriaOnSession[] - Rating Rating? + rating Rating? } model CriteriaOnSession { @@ -84,7 +84,7 @@ model Candidate { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions CandidateOnSession[] - Rating Rating? + rating Rating? } model CandidateOnSession { diff --git a/rating-system/prisma/user.ts b/rating-system/prisma/user.ts index 5c07bbb..80f5eb3 100644 --- a/rating-system/prisma/user.ts +++ b/rating-system/prisma/user.ts @@ -1,5 +1,5 @@ import prisma from './prisma' -import { hash } from 'argon2' +import { hash, verify } from 'argon2' export const getAllUsers = async () => { const users = await prisma.user.findMany({}) @@ -15,6 +15,7 @@ export const getUser = async (id: string) => { export const createUser = async (email: string, name: string, password: string) => { const hashedPassword = await hash(password); + console.log(email, name, hashedPassword) const user = await prisma.user.create({ data: { email, @@ -47,4 +48,16 @@ export const deleteUser = async (id: string) => { } }) return user +} + +export const login = async (email: string, password: string) => { + const user = await prisma.user.findUnique({ + where: { + email + } + }) + if (!user) throw new Error('User not found') + const valid = await verify(user.password, password) + if (!valid) throw new Error('Invalid password') + return user } \ No newline at end of file From 81938babe0e0478cea8858e0d4ae4e0b1c2f16ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCmeyye=20Akka=C5=9F?= Date: Mon, 22 May 2023 23:51:37 +0300 Subject: [PATCH 3/7] degisiklikler --- rating-system/docker-compose.yaml | 17 ++++++ rating-system/pages/index.tsx | 77 ++++++++++++++------------ rating-system/pages/login/index.tsx | 82 +++++++++++++++++----------- rating-system/pages/signup/index.tsx | 75 +++++++++++++++---------- 4 files changed, 155 insertions(+), 96 deletions(-) create mode 100644 rating-system/docker-compose.yaml diff --git a/rating-system/docker-compose.yaml b/rating-system/docker-compose.yaml new file mode 100644 index 0000000..8bc5f11 --- /dev/null +++ b/rating-system/docker-compose.yaml @@ -0,0 +1,17 @@ +version: "3.8" +services: + database: + image: "bitnami/mongodb:latest" + environment: + - MONGODB_ADVERTISED_HOSTNAME=127.0.0.1 + - MONGODB_REPLICA_SET_MODE=primary + - MONGODB_ROOT_USER=user + - MONGODB_ROOT_PASSWORD=hello + - MONGODB_REPLICA_SET_KEY=replicasetkey123 + ports: + - "27017:27017" + volumes: + - "mongo-db:/bitnami/mongodb" + +volumes: + mongo-db: \ No newline at end of file diff --git a/rating-system/pages/index.tsx b/rating-system/pages/index.tsx index 12dfe56..0cab7eb 100644 --- a/rating-system/pages/index.tsx +++ b/rating-system/pages/index.tsx @@ -1,51 +1,60 @@ -import React, { useEffect } from 'react' -import { useAppDispatch, useAppSelector } from '../store/hooks' -import { RootState } from '../store' -import { setUser } from '../store/actions/user' +import React from 'react'; +import { useAppDispatch, useAppSelector } from '../store/hooks'; +import { RootState } from '../store'; +import { setUser } from '../store/actions/user'; + +interface UserData { + name: string; + email: string; + password: string; +} const Index = () => { const dispatch = useAppDispatch(); const user = useAppSelector((state: RootState) => state.user.user); - - const [name, setName] = React.useState('') - const [email, setEmail] = React.useState('') - const [password, setPassword] = React.useState('') - - const handleNameChange = (e: React.ChangeEvent) => { - setName(e.target.value) - } - - const handleEmailChange = (e: React.ChangeEvent) => { - setEmail(e.target.value) - } - - const handlePasswordChange = (e: React.ChangeEvent) => { - setPassword(e.target.value) - } - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - dispatch(setUser(name, email, password)) - } + const [userData, setUserData] = React.useState({ name: '', email: '', password: '' }); + const [isEmailValid, setIsEmailValid] = React.useState(true); + + const validateEmail = (email: string): boolean => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const handleChange = (field: T, value: UserData[T]): void => { + if (field === 'email') { + const isValid = validateEmail(value); + setIsEmailValid(isValid); + } + setUserData((prevUserData) => ({ ...prevUserData, [field]: value })); + }; + + const handleSubmit = (e: React.FormEvent): void => { + e.preventDefault(); + if (!isEmailValid) { + alert('Lütfen geçerli bir e-posta adresi girin.'); + return; + } + dispatch(setUser(userData.name, userData.email, userData.password)); + }; return (

Rating System

- { - user &&
+ {user && ( +
user: {user.name}
email: {user.email}
password: {user.password}
- } - - - + )} + handleChange('name', e.target.value)} /> + handleChange('email', e.target.value)} /> + handleChange('password', e.target.value)} />
- ) -} + ); +}; -export default Index \ No newline at end of file +export default Index; \ No newline at end of file diff --git a/rating-system/pages/login/index.tsx b/rating-system/pages/login/index.tsx index cef1cca..987ff9d 100644 --- a/rating-system/pages/login/index.tsx +++ b/rating-system/pages/login/index.tsx @@ -1,39 +1,57 @@ -import { User } from "@prisma/client" -import { useRouter } from "next/router" -import { useState } from "react" -import { fetchPost } from "../../fetch-helpers/post" +import { User } from "@prisma/client"; +import { useRouter } from "next/router"; +import React from 'react'; +import { fetchPost } from "../../fetch-helpers/post"; + +interface UserData { + email: string; + password: string; +} const Login = () => { - const router = useRouter() - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') + const router = useRouter(); + const [userData, setUserData] = React.useState({email: "", password: ""}); - const handleEmailChange = (e: React.ChangeEvent) => { - setEmail(e.target.value) - } + const handleChange = ( + field: T, + value: UserData[T] + ): void => { + setUserData({ ...userData, [field]: value }); + }; - const handlePasswordChange = (e: React.ChangeEvent) => { - setPassword(e.target.value) + const handleSubmit = async ( + e: React.FormEvent + ): Promise => { + e.preventDefault(); + console.log(userData); + try { + const result = await fetchPost("/api/login", userData); + if (result && result.id) { + router.push("/home"); + } + } catch (error) { + console.error("Error:", error); } + }; - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - const result = await fetchPost('/api/login', { email, password } as User) - if (result && result.id) { - router.push('/home') - } - } - - return ( - <> -

Login

-
- - - -
- - ) -} + return ( + <> +

Login

+
+ handleChange("email", e.target.value)} + /> + handleChange("password", e.target.value)} + /> + +
+ + ); +}; -export default Login \ No newline at end of file +export default Login; \ No newline at end of file diff --git a/rating-system/pages/signup/index.tsx b/rating-system/pages/signup/index.tsx index 8f7e6e9..ce7a75d 100644 --- a/rating-system/pages/signup/index.tsx +++ b/rating-system/pages/signup/index.tsx @@ -1,43 +1,58 @@ -import { User } from '@prisma/client' -import { useRouter } from 'next/router' -import React from 'react' -import { fetchPost } from '../../fetch-helpers/post' +import { User } from '@prisma/client'; +import { useRouter } from 'next/router'; +import React from 'react'; +import { fetchPost } from '../../fetch-helpers/post'; -const Signup = () => { - const router = useRouter() - const [name, setName] = React.useState('') - const [email, setEmail] = React.useState('') - const [password, setPassword] = React.useState('') +interface UserData { + name: string; + email: string; + password: string; +} - const handleNameChange = (e: React.ChangeEvent) => { - setName(e.target.value) - } +const Signup: React.FC = () => { + const router = useRouter(); + const [userData, setUserData] = React.useState({ name: '', email: '', password: '' }); + const [isEmailValid, setIsEmailValid] = React.useState(true); - const handleEmailChange = (e: React.ChangeEvent) => { - setEmail(e.target.value) - } + const validateEmail = (email: string): boolean => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; - const handlePasswordChange = (e: React.ChangeEvent) => { - setPassword(e.target.value) - } + const handleChange = (field: T, value: UserData[T]): void => { + if (field === 'email') { + const isValid = validateEmail(value); + setIsEmailValid(isValid); + } + setUserData((prevUserData) => ({ ...prevUserData, [field]: value })); + }; - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - const result = await fetchPost('/api/user', { name, email, password } as User) - if (result && result.id) { - router.push('/login') + const handleSubmit = async (e: React.FormEvent): Promise => { + e.preventDefault(); + if (!isEmailValid) { + alert('Lütfen geçerli bir e-posta adresi girin.'); + return; } - } + console.log(userData); + try { + const result = await fetchPost('/api/user', userData); + if (result && result.id) { + router.push('/login'); + } + } catch (error) { + console.error('Error:', error); + } + }; return (

Rating System

- - - + handleChange('name', e.target.value)} /> + handleChange('email', e.target.value)} /> + handleChange('password', e.target.value)} />
- ) -} + ); +}; -export default Signup \ No newline at end of file +export default Signup; \ No newline at end of file From cb3ddf17f7e55caf73294f260e76d5c5a874fb0a Mon Sep 17 00:00:00 2001 From: Muhtalip Dede Date: Thu, 25 May 2023 22:48:16 +0300 Subject: [PATCH 4/7] =?UTF-8?q?2.=20yay=C4=B1n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating-system/.dockerignore | 7 + rating-system/Dockerfile | 45 ++++++ rating-system/components/Button.tsx | 11 +- rating-system/components/Card.tsx | 20 +++ rating-system/components/ErrorBoundary.ts | 34 ++++ rating-system/components/Input.tsx | 14 +- rating-system/components/Sidebar.tsx | 12 ++ rating-system/components/layout/Layout.tsx | 17 ++ rating-system/fly.toml | 37 +++++ rating-system/middleware.ts | 12 +- rating-system/middlewares/auth.ts | 28 ++++ rating-system/middlewares/request-log.ts | 15 ++ rating-system/package-lock.json | 146 ++++++++++++++++++ rating-system/package.json | 3 + rating-system/pages/_app.tsx | 23 ++- rating-system/pages/api/criteria.ts | 22 +++ rating-system/pages/api/me/me.ts | 11 ++ rating-system/pages/api/me/type.ts | 14 ++ rating-system/pages/api/register.ts | 20 +++ rating-system/pages/api/session.ts | 22 +++ rating-system/pages/api/user.ts | 26 +--- rating-system/pages/criteria/create.tsx | 28 ++++ rating-system/pages/home/index.tsx | 96 +++++++++++- rating-system/pages/login/index.tsx | 18 ++- rating-system/pages/session/create.tsx | 25 +++ rating-system/pages/signup/index.tsx | 22 +-- rating-system/pages/user/[id].tsx | 49 ++++++ rating-system/pages/user/list.tsx | 40 +++++ rating-system/prisma/admin.ts | 10 ++ rating-system/prisma/candidate.ts | 10 ++ rating-system/prisma/criteria.ts | 16 ++ rating-system/prisma/participant.ts | 10 ++ rating-system/prisma/schema.prisma | 1 + rating-system/prisma/session.ts | 15 ++ rating-system/prisma/user-type.ts | 27 ++++ rating-system/prisma/user.ts | 63 ++++---- rating-system/public/globals.css | 28 +--- rating-system/store/actions/user.ts | 6 +- rating-system/utils/fetch/get.ts | 17 ++ .../{fetch-helpers => utils/fetch}/post.ts | 5 + rating-system/utils/jwt/decode.ts | 7 + rating-system/utils/jwt/user.ts | 17 ++ .../utils/local-storage/local-storage.ts | 14 ++ 43 files changed, 950 insertions(+), 113 deletions(-) create mode 100644 rating-system/.dockerignore create mode 100644 rating-system/Dockerfile create mode 100644 rating-system/components/Card.tsx create mode 100644 rating-system/components/ErrorBoundary.ts create mode 100644 rating-system/components/Sidebar.tsx create mode 100644 rating-system/components/layout/Layout.tsx create mode 100644 rating-system/fly.toml create mode 100644 rating-system/middlewares/auth.ts create mode 100644 rating-system/middlewares/request-log.ts create mode 100644 rating-system/pages/api/criteria.ts create mode 100644 rating-system/pages/api/me/me.ts create mode 100644 rating-system/pages/api/me/type.ts create mode 100644 rating-system/pages/api/register.ts create mode 100644 rating-system/pages/api/session.ts create mode 100644 rating-system/pages/criteria/create.tsx create mode 100644 rating-system/pages/session/create.tsx create mode 100644 rating-system/pages/user/[id].tsx create mode 100644 rating-system/pages/user/list.tsx create mode 100644 rating-system/prisma/admin.ts create mode 100644 rating-system/prisma/candidate.ts create mode 100644 rating-system/prisma/criteria.ts create mode 100644 rating-system/prisma/participant.ts create mode 100644 rating-system/prisma/session.ts create mode 100644 rating-system/prisma/user-type.ts create mode 100644 rating-system/utils/fetch/get.ts rename rating-system/{fetch-helpers => utils/fetch}/post.ts (64%) create mode 100644 rating-system/utils/jwt/decode.ts create mode 100644 rating-system/utils/jwt/user.ts create mode 100644 rating-system/utils/local-storage/local-storage.ts diff --git a/rating-system/.dockerignore b/rating-system/.dockerignore new file mode 100644 index 0000000..c550055 --- /dev/null +++ b/rating-system/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git diff --git a/rating-system/Dockerfile b/rating-system/Dockerfile new file mode 100644 index 0000000..d002815 --- /dev/null +++ b/rating-system/Dockerfile @@ -0,0 +1,45 @@ +# Install dependencies only when needed +FROM node:16-alpine AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY . . +RUN npm install --frozen-lockfile + +# If using npm with a `package-lock.json` comment out above and use below instead +# RUN npm ci + +ENV NEXT_TELEMETRY_DISABLED 1 + +# Add `ARG` instructions below if you need `NEXT_PUBLIC_` variables +# then put the value on your fly.toml +# Example: +# ARG NEXT_PUBLIC_EXAMPLE="value here" + +RUN npm run build + +# If using npm comment out above and use below instead +# RUN npm run build + +# Production image, copy all the files and run next +FROM node:16-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production +ENV NEXT_TELEMETRY_DISABLED 1 +ENV DATABASE_URL mongodb+srv://futureverseadmin:futureverseadmin@cluster0.yhcqmi3.mongodb.net/rating-system-session?retryWrites=true&w=majority +ENV JWT_SECRET secret + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +RUN npx prisma generate + +COPY --from=builder /app ./ +COPY --from=builder /app/node_modules/.prisma /app/node_modules/.prisma + +USER nextjs + +CMD ["npm", "run", "start"] + +# If using npm comment out above and use below instead +# CMD ["npm", "run", "start"] diff --git a/rating-system/components/Button.tsx b/rating-system/components/Button.tsx index cfd9f4b..d45c84a 100644 --- a/rating-system/components/Button.tsx +++ b/rating-system/components/Button.tsx @@ -1,8 +1,15 @@ import React from 'react' -const Button = () => { +export type ButtonProps = { + children: React.ReactNode + onClick?: (e: React.FormEvent) => void +} + +const Button = ({ children, onClick }: ButtonProps) => { return ( - + ) } diff --git a/rating-system/components/Card.tsx b/rating-system/components/Card.tsx new file mode 100644 index 0000000..b1575bc --- /dev/null +++ b/rating-system/components/Card.tsx @@ -0,0 +1,20 @@ +export type CardProps = { + children: React.ReactNode + title?: string +} + +const CardLine = () => ( +
+) + +const Card = ({ children, title }: CardProps) => ( +
+
+ {title} + + {children} +
+
+) + +export default Card \ No newline at end of file diff --git a/rating-system/components/ErrorBoundary.ts b/rating-system/components/ErrorBoundary.ts new file mode 100644 index 0000000..3a4e009 --- /dev/null +++ b/rating-system/components/ErrorBoundary.ts @@ -0,0 +1,34 @@ +import React from 'react' + +interface ErrorBoundaryProps { + children: React.ReactNode + fallback?: React.ReactNode +} + +interface ErrorBoundaryState { + hasError: boolean +} + +class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props) + this.state = { hasError: false } + } + + static getDerivedStateFromError(error: any) { + return { hasError: true } + } + + componentDidCatch(error: any, errorInfo: any) { + console.log(error, errorInfo) + } + + render() { + if (this.state.hasError) { + return this.props.fallback + } + return this.props.children + } +} + +export default ErrorBoundary \ No newline at end of file diff --git a/rating-system/components/Input.tsx b/rating-system/components/Input.tsx index b6a9023..c2452f7 100644 --- a/rating-system/components/Input.tsx +++ b/rating-system/components/Input.tsx @@ -1,8 +1,18 @@ import React from 'react' -const Input = (props: any) => { +export type InputProps = { + value: string + onChange: (e: React.ChangeEvent) => void + placeholder: string + type?: string, + required?: boolean +} + +const Input = ({ value, onChange, placeholder, type, required }: InputProps) => { return ( - + <> + + ) } diff --git a/rating-system/components/Sidebar.tsx b/rating-system/components/Sidebar.tsx new file mode 100644 index 0000000..38bfcdc --- /dev/null +++ b/rating-system/components/Sidebar.tsx @@ -0,0 +1,12 @@ +export type SidebarProps = { + children: React.ReactNode +} + +const Sidebar = ({ children }: SidebarProps) => { + return ( + <> + + ) +} + +export default Sidebar \ No newline at end of file diff --git a/rating-system/components/layout/Layout.tsx b/rating-system/components/layout/Layout.tsx new file mode 100644 index 0000000..f1538e4 --- /dev/null +++ b/rating-system/components/layout/Layout.tsx @@ -0,0 +1,17 @@ +export type LayoutProps = { + children: React.ReactNode +} + +const Layout = ({ children }: LayoutProps) => { + return ( + <> +
+
+ {children} +
+
+ + ) +} + +export default Layout \ No newline at end of file diff --git a/rating-system/fly.toml b/rating-system/fly.toml new file mode 100644 index 0000000..f074275 --- /dev/null +++ b/rating-system/fly.toml @@ -0,0 +1,37 @@ +# fly.toml file generated for rating-system on 2023-05-25T20:23:08+03:00 + +app = "rating-system" +kill_signal = "SIGINT" +kill_timeout = 5 +processes = [] + +[env] + +[experimental] + auto_rollback = true + +[[services]] + http_checks = [] + internal_port = 3000 + processes = ["app"] + protocol = "tcp" + script_checks = [] + [services.concurrency] + hard_limit = 25 + soft_limit = 20 + type = "connections" + + [[services.ports]] + force_https = true + handlers = ["http"] + port = 80 + + [[services.ports]] + handlers = ["tls", "http"] + port = 443 + + [[services.tcp_checks]] + grace_period = "1s" + interval = "15s" + restart_limit = 0 + timeout = "2s" diff --git a/rating-system/middleware.ts b/rating-system/middleware.ts index de0980f..ef861f5 100644 --- a/rating-system/middleware.ts +++ b/rating-system/middleware.ts @@ -1,13 +1,9 @@ import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; +import addRequestLogMiddleware from './middlewares/request-log'; +import addAuthMiddleware from './middlewares/auth'; export function middleware(request: NextRequest, response: NextResponse) { - const { url, method, body, nextUrl } = request; - - if (nextUrl.pathname.startsWith('/api')) { - console.log('---'); - console.log(`[${method}] ${url}`); - console.log(body); - console.log('---'); - } + addRequestLogMiddleware(request, response); + addAuthMiddleware(request, response); } \ No newline at end of file diff --git a/rating-system/middlewares/auth.ts b/rating-system/middlewares/auth.ts new file mode 100644 index 0000000..27e9c63 --- /dev/null +++ b/rating-system/middlewares/auth.ts @@ -0,0 +1,28 @@ +import { NextRequest, NextResponse } from "next/server"; +import * as jose from 'jose' + +export default async function addAuthMiddleware(req: NextRequest, res: NextResponse) { + const { nextUrl } = req; + if (nextUrl.pathname.startsWith('/api') && !nextUrl.pathname.startsWith('/api/register') && !nextUrl.pathname.startsWith('/api/login')) { + const token = req.headers.get('authorization'); + const tokenType = token?.split(' ')[0] as string; + + if (tokenType !== 'Bearer') { + throw new Error('Invalid token type'); + } + + const bearerToken = token?.split(' ')[1] as string; + if (!token || !bearerToken) { + throw new Error('No token'); + } + + const key = new TextEncoder().encode(process.env.JWT_SECRET); + const verifiedToken = await jose.jwtVerify(bearerToken, key); + + if (!verifiedToken) { + throw new Error('Invalid token'); + } + + return NextResponse.next(); + } +} \ No newline at end of file diff --git a/rating-system/middlewares/request-log.ts b/rating-system/middlewares/request-log.ts new file mode 100644 index 0000000..975d891 --- /dev/null +++ b/rating-system/middlewares/request-log.ts @@ -0,0 +1,15 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export default function addRequestLogMiddleware(req: NextRequest, res: NextResponse) { + const { url, method, body, nextUrl } = req; + + if (nextUrl.pathname.startsWith('/api')) { + console.log('---'); + console.log(`[${method}] ${url}`); + console.log(body); + console.log('---'); + } + + return NextResponse.next(); +} \ No newline at end of file diff --git a/rating-system/package-lock.json b/rating-system/package-lock.json index 111ca4f..6f81bbd 100644 --- a/rating-system/package-lock.json +++ b/rating-system/package-lock.json @@ -15,6 +15,7 @@ "@types/react-dom": "18.2.4", "argon2": "^0.30.3", "autoprefixer": "10.4.14", + "jose": "^4.14.4", "next": "13.4.2", "postcss": "8.4.23", "react": "18.2.0", @@ -24,6 +25,8 @@ "typescript": "5.0.4" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.2", + "jsonwebtoken": "^9.0.0", "prisma": "^4.14.0" } }, @@ -368,6 +371,15 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", @@ -575,6 +587,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -744,6 +762,15 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.394", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz", @@ -1042,11 +1069,56 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", + "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -1060,6 +1132,12 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2328,6 +2406,15 @@ "hoist-non-react-statics": "^3.3.0" } }, + "@types/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", @@ -2478,6 +2565,12 @@ "update-browserslist-db": "^1.0.10" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2589,6 +2682,15 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "electron-to-chromium": { "version": "1.4.394", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz", @@ -2818,11 +2920,49 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==" }, + "jose": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", + "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dev": true, + "requires": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -2833,6 +2973,12 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", diff --git a/rating-system/package.json b/rating-system/package.json index 2f6d5a4..aef9ea0 100644 --- a/rating-system/package.json +++ b/rating-system/package.json @@ -16,6 +16,7 @@ "@types/react-dom": "18.2.4", "argon2": "^0.30.3", "autoprefixer": "10.4.14", + "jose": "^4.14.4", "next": "13.4.2", "postcss": "8.4.23", "react": "18.2.0", @@ -25,6 +26,8 @@ "typescript": "5.0.4" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.2", + "jsonwebtoken": "^9.0.0", "prisma": "^4.14.0" } } diff --git a/rating-system/pages/_app.tsx b/rating-system/pages/_app.tsx index 2d22f7e..56f5c4b 100644 --- a/rating-system/pages/_app.tsx +++ b/rating-system/pages/_app.tsx @@ -1,9 +1,30 @@ import type { AppProps } from 'next/app' import { Provider } from 'react-redux'; import store from '../store'; +import '../public/globals.css' +import ErrorBoundary from '../components/ErrorBoundary'; +import { useEffect } from 'react'; +import { getLocalStorage } from '../utils/local-storage/local-storage'; +import { useRouter } from 'next/router'; +import Layout from '../components/layout/Layout'; export default function App({ Component, pageProps }: AppProps) { + const router = useRouter() + const Fallback = () =>
Something went wrong.
+ + useEffect(() => { + if (router.pathname === '/login' || router.pathname === '/signup') return; + const token = getLocalStorage('token'); + if (!token) { + router.push('/login') + } + }, []) + return - + }> + + + + } \ No newline at end of file diff --git a/rating-system/pages/api/criteria.ts b/rating-system/pages/api/criteria.ts new file mode 100644 index 0000000..2b29aab --- /dev/null +++ b/rating-system/pages/api/criteria.ts @@ -0,0 +1,22 @@ +import { NextApiRequest, NextApiResponse } from "next/types" +import { createCriteria, getCriterias } from "../../prisma/criteria"; + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + try { + switch (req.method) { + case 'GET': { + const criterias = await getCriterias(); + return res.json(criterias) + } + case 'POST': { + const { title, description } = req.body + const criteria = await createCriteria(title, description) + return res.json(criteria) + } + default: + return res.status(405).end(`Method ${req.method} Not Allowed`) + } + } catch (error: any) { + return res.status(500).json({ ...error, message: error.message }) + } +} \ No newline at end of file diff --git a/rating-system/pages/api/me/me.ts b/rating-system/pages/api/me/me.ts new file mode 100644 index 0000000..f015d79 --- /dev/null +++ b/rating-system/pages/api/me/me.ts @@ -0,0 +1,11 @@ +import { NextApiRequest, NextApiResponse } from "next/types"; +import { getUser } from "../../../utils/jwt/user"; + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + try { + const user = await getUser(req); + return res.status(200).json(user); + } catch (error: any) { + return res.status(500).json({ ...error, message: error.message }) + } +} diff --git a/rating-system/pages/api/me/type.ts b/rating-system/pages/api/me/type.ts new file mode 100644 index 0000000..5aef81d --- /dev/null +++ b/rating-system/pages/api/me/type.ts @@ -0,0 +1,14 @@ +import { NextApiRequest, NextApiResponse } from "next/types"; +import { getUserType } from "../../../prisma/user-type"; +import { decode } from "../../../utils/jwt/decode"; +import { getUser } from "../../../utils/jwt/user"; + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + try { + const user = await getUser(req); + const userType = await getUserType(user.id); + return res.status(200).json(userType); + } catch (error: any) { + return res.status(500).json({ ...error, message: error.message }) + } +} diff --git a/rating-system/pages/api/register.ts b/rating-system/pages/api/register.ts new file mode 100644 index 0000000..17d2053 --- /dev/null +++ b/rating-system/pages/api/register.ts @@ -0,0 +1,20 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import { + createUser, +} from '../../prisma/user' + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + try { + switch (req.method) { + case 'POST': { + const { email, name, password } = req.body + const user = await createUser(email, name, password) + return res.json(user) + } + default: + return res.status(405).end(`Method ${req.method} Not Allowed`) + } + } catch (error: any) { + return res.status(500).json({ ...error, message: error.message }) + } +} \ No newline at end of file diff --git a/rating-system/pages/api/session.ts b/rating-system/pages/api/session.ts new file mode 100644 index 0000000..3ea4892 --- /dev/null +++ b/rating-system/pages/api/session.ts @@ -0,0 +1,22 @@ +import { NextApiRequest, NextApiResponse } from "next/types" +import { createSession, getSessions } from "../../prisma/session" + +export default async function handle(req: NextApiRequest, res: NextApiResponse) { + try { + switch (req.method) { + case 'GET': { + const sessions = await getSessions(); + return res.json(sessions) + } + case 'POST': { + const { name } = req.body + const session = await createSession(name) + return res.json(session) + } + default: + return res.status(405).end(`Method ${req.method} Not Allowed`) + } + } catch (error: any) { + return res.status(500).json({ ...error, message: error.message }) + } +} \ No newline at end of file diff --git a/rating-system/pages/api/user.ts b/rating-system/pages/api/user.ts index c1af95e..7bfdc7d 100644 --- a/rating-system/pages/api/user.ts +++ b/rating-system/pages/api/user.ts @@ -1,40 +1,22 @@ import { NextApiRequest, NextApiResponse } from 'next' import { - createUser, - deleteUser, getAllUsers, getUser, - updateUser } from '../../prisma/user' export default async function handle(req: NextApiRequest, res: NextApiResponse) { try { switch (req.method) { case 'GET': { - if (req.query.id) { - const user = await getUser(req.query.id as string) + const { id } = req.query + if (id) { + const user = await getUser(id as string) return res.status(200).json(user) } else { const users = await getAllUsers() - return res.json(users) + return res.status(200).json(users) } } - case 'POST': { - const { email, name, password } = req.body - console.log(email, name, password); - const user = await createUser(email, name, password) - return res.json(user) - } - case 'PUT': { - const { id, name, email, password } = req.body - const user = await updateUser(id, email, name, password) - return res.json(user) - } - case 'DELETE': { - const { id } = req.body - const user = await deleteUser(id) - return res.json(user) - } default: return res.status(405).end(`Method ${req.method} Not Allowed`) } diff --git a/rating-system/pages/criteria/create.tsx b/rating-system/pages/criteria/create.tsx new file mode 100644 index 0000000..cfb21c4 --- /dev/null +++ b/rating-system/pages/criteria/create.tsx @@ -0,0 +1,28 @@ +import { Criteria } from "@prisma/client"; +import { useState } from "react"; +import { fetchPost } from "../../utils/fetch/post"; + +export default function CreateCriteria() { + const [criteriaName, setCriteriaName] = useState(""); + const [criteriaDescription, setCriteriaDescription] = useState(""); + + const createCriteria = async () => { + const criteria = await fetchPost('/api/criteria', { title: criteriaName, description: criteriaDescription } as Criteria) + console.log(criteria) + } + + return ( + <> +

Create Criteria

+
+ Criteria Title + setCriteriaName(e.target.value)} /> + Criteria Description +