From a62455b49def24f72a0ef3c5646480535faaad82 Mon Sep 17 00:00:00 2001 From: yuqi88 Date: Tue, 29 Mar 2022 05:39:16 -0700 Subject: [PATCH 01/19] styling submit button --- src/pages/puzzles/[instanceId]/index.tsx | 36 +++++++++++++----------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/pages/puzzles/[instanceId]/index.tsx b/src/pages/puzzles/[instanceId]/index.tsx index d64af96..01da8ff 100644 --- a/src/pages/puzzles/[instanceId]/index.tsx +++ b/src/pages/puzzles/[instanceId]/index.tsx @@ -169,23 +169,25 @@ const PuzzlePage = ({ setAnswer={setAnswer} /> - {isAuthenticated ? ( - - ); -} +}; export default SignUpDialog; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index e2b4c81..6ea12ec 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -9,7 +9,6 @@ const MyApp = ({ Component, pageProps: { session, initStore, ...pageProps } }: AppProps) => { - const TOAST_LIMIT = 1; const { toasts } = useToasterStore(); @@ -17,15 +16,15 @@ const MyApp = ({ // Solution is proposed by @timolins from https://github.com/timolins/react-hot-toast/issues/31#issuecomment-803359550 useEffect(() => { toasts - .filter((t) => t.visible) + .filter(t => t.visible) .filter((_, i) => i >= TOAST_LIMIT) - .forEach((t) => toast.dismiss(t.id)); + .forEach(t => toast.dismiss(t.id)); }, [toasts]); return ( - - - + + + ); }; diff --git a/src/pages/auth/signup/index.tsx b/src/pages/auth/signup/index.tsx index 8ffab87..1a5a45b 100644 --- a/src/pages/auth/signup/index.tsx +++ b/src/pages/auth/signup/index.tsx @@ -27,9 +27,7 @@ const SignUpPage = () => { return false; } if (status === 'authenticated') { - toast(t => ( - - )); + toast(t => ); return false; } else { signUp({ email, password }); diff --git a/src/pages/puzzles/[instanceId]/index.tsx b/src/pages/puzzles/[instanceId]/index.tsx index 01da8ff..52317c9 100644 --- a/src/pages/puzzles/[instanceId]/index.tsx +++ b/src/pages/puzzles/[instanceId]/index.tsx @@ -171,21 +171,21 @@ const PuzzlePage = ({
{isAuthenticated ? ( -
diff --git a/src/styles/pages/PuzzlePage.module.scss b/src/styles/pages/PuzzlePage.module.scss index 2c0d32c..5bf133d 100644 --- a/src/styles/pages/PuzzlePage.module.scss +++ b/src/styles/pages/PuzzlePage.module.scss @@ -92,50 +92,44 @@ margin: 3rem 0; } - /* MEDIA QUERIES*/ @media (max-width: 600px) { - .card{ + .card { max-width: 600px; flex-direction: column; } } @media (max-width: 768px) { - .card{ + .card { max-width: 720px; flex-direction: column; } } @media (max-width: 992px) { - .card{ + .card { max-width: 960px; flex-direction: column; - } } @media (max-width: 1200px) { .card { max-width: 1140px; } - .text{ + .text { flex: 0 0 65%; } - .image{ + .image { flex: 0 0 35%; } } @media (min-width: 1200px) { - .card{ + .card { min-width: 1140px; } - .text{ + .text { flex: 0 0 65%; } - .image{ - flex: 0 0 35%; + .image { + flex: 0 0 35%; } - } - - - From 9b9355a7decf77a3eb5de949ffc8900333c4da81 Mon Sep 17 00:00:00 2001 From: yuqi88 Date: Tue, 29 Mar 2022 11:54:13 -0700 Subject: [PATCH 04/19] add admin header --- src/components/Global/AdminHeader/index.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/components/Global/AdminHeader/index.tsx diff --git a/src/components/Global/AdminHeader/index.tsx b/src/components/Global/AdminHeader/index.tsx new file mode 100644 index 0000000..8c3ba2e --- /dev/null +++ b/src/components/Global/AdminHeader/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import styles from './AdminHeader.module.scss' + +const AdminHeader = () => { + const currentPage = 'Admin Dashboard'; + return ( +
+
+

{currentPage}

+
+
+ ); +}; + +export default AdminHeader; \ No newline at end of file From 8f9f0cfa90a051702166450965ba2939d68ff0e9 Mon Sep 17 00:00:00 2001 From: yuqi88 Date: Tue, 29 Mar 2022 11:54:27 -0700 Subject: [PATCH 05/19] add styling for admin header --- .../Global/AdminHeader/AdminHeader.module.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/components/Global/AdminHeader/AdminHeader.module.scss diff --git a/src/components/Global/AdminHeader/AdminHeader.module.scss b/src/components/Global/AdminHeader/AdminHeader.module.scss new file mode 100644 index 0000000..1a9280d --- /dev/null +++ b/src/components/Global/AdminHeader/AdminHeader.module.scss @@ -0,0 +1,13 @@ +@import '../../../styles/abstracts/_mixins/text'; +@import '../../../styles/abstracts/variables'; + +.pageName { + @include font-header-extra-bold; + width: 100vw; + height: 3.4rem; + padding: 0 1.5rem 3rem; + font-size: 1.5rem; + border-bottom: $grey-transparency 1px solid; + + +} \ No newline at end of file From 0ddd6bf338a041b5fba2d2755a52da172a119ae0 Mon Sep 17 00:00:00 2001 From: yuqi88 Date: Tue, 29 Mar 2022 11:57:22 -0700 Subject: [PATCH 06/19] add admin header to admin page --- src/components/Global/index.tsx | 4 +++- src/pages/admin/index.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Global/index.tsx b/src/components/Global/index.tsx index 8e97c52..06ca77e 100644 --- a/src/components/Global/index.tsx +++ b/src/components/Global/index.tsx @@ -10,6 +10,7 @@ import LogStatusButton from './LogStatusButton'; import SearchAndFilter from './SearchAndFilter'; import Difficulty from './Difficulty'; import Loading from './Loading'; +import AdminHeader from "./AdminHeader"; export { Button, @@ -23,5 +24,6 @@ export { LogStatusButton, SearchAndFilter, Difficulty, - Loading + Loading, + AdminHeader, }; diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index ca574c8..917dffd 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -5,7 +5,7 @@ import { useSession } from 'next-auth/react'; import { PuzzleGenerate } from '../../components/App'; import { getAllPuzzles, isAdmin } from '../../services'; import { GetServerSideProps } from 'next'; -import { Header } from '../../components/Global'; +import {AdminHeader, Header} from '../../components/Global'; const Admin = ({ puzzlesList }) => { const { data: session, status } = useSession(); @@ -34,6 +34,7 @@ const Admin = ({ puzzlesList }) => { return ( <>
+

ADMIN PAGE 🤓

{/** TODO: Create Header for admin page */}
From cef54a927223b89d0a8707522abf4398bb9c9adc Mon Sep 17 00:00:00 2001 From: yuqi88 Date: Tue, 29 Mar 2022 12:10:03 -0700 Subject: [PATCH 07/19] run prettify --- .../AdminHeader/AdminHeader.module.scss | 4 +--- src/components/Global/AdminHeader/index.tsx | 20 +++++++++---------- src/components/Global/index.tsx | 4 ++-- src/pages/admin/index.tsx | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/components/Global/AdminHeader/AdminHeader.module.scss b/src/components/Global/AdminHeader/AdminHeader.module.scss index 1a9280d..0e65bac 100644 --- a/src/components/Global/AdminHeader/AdminHeader.module.scss +++ b/src/components/Global/AdminHeader/AdminHeader.module.scss @@ -8,6 +8,4 @@ padding: 0 1.5rem 3rem; font-size: 1.5rem; border-bottom: $grey-transparency 1px solid; - - -} \ No newline at end of file +} diff --git a/src/components/Global/AdminHeader/index.tsx b/src/components/Global/AdminHeader/index.tsx index 8c3ba2e..b8aa90d 100644 --- a/src/components/Global/AdminHeader/index.tsx +++ b/src/components/Global/AdminHeader/index.tsx @@ -1,15 +1,15 @@ import React from 'react'; -import styles from './AdminHeader.module.scss' +import styles from './AdminHeader.module.scss'; const AdminHeader = () => { - const currentPage = 'Admin Dashboard'; - return ( -
-
-

{currentPage}

-
-
- ); + const currentPage = 'Admin Dashboard'; + return ( +
+
+

{currentPage}

+
+
+ ); }; -export default AdminHeader; \ No newline at end of file +export default AdminHeader; diff --git a/src/components/Global/index.tsx b/src/components/Global/index.tsx index 06ca77e..aa84f10 100644 --- a/src/components/Global/index.tsx +++ b/src/components/Global/index.tsx @@ -10,7 +10,7 @@ import LogStatusButton from './LogStatusButton'; import SearchAndFilter from './SearchAndFilter'; import Difficulty from './Difficulty'; import Loading from './Loading'; -import AdminHeader from "./AdminHeader"; +import AdminHeader from './AdminHeader'; export { Button, @@ -25,5 +25,5 @@ export { SearchAndFilter, Difficulty, Loading, - AdminHeader, + AdminHeader }; diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index 917dffd..bc05f85 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -5,7 +5,7 @@ import { useSession } from 'next-auth/react'; import { PuzzleGenerate } from '../../components/App'; import { getAllPuzzles, isAdmin } from '../../services'; import { GetServerSideProps } from 'next'; -import {AdminHeader, Header} from '../../components/Global'; +import { AdminHeader, Header } from '../../components/Global'; const Admin = ({ puzzlesList }) => { const { data: session, status } = useSession(); @@ -34,7 +34,7 @@ const Admin = ({ puzzlesList }) => { return ( <>
- +

ADMIN PAGE 🤓

{/** TODO: Create Header for admin page */}
From d711c3f774bb8b271f4aece294ed18ae2b020db4 Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Tue, 12 Apr 2022 18:40:57 +0700 Subject: [PATCH 08/19] Dockerization --- Dockerfile | 17 ++++++++++++++--- docker-compose.yml | 19 ++++++++++++++++--- package.json | 5 ++++- src/pages/puzzles/map/index.tsx | 8 ++++---- tsconfig.json | 2 +- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index d1f6e11..c93a629 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,22 @@ -FROM node:14 +FROM node:16 WORKDIR . -COPY package*.json . -RUN npm i --production +ARG DATABASE_URL +ARG GOOGLE_CLIENT_ID +ARG GOOGLE_CLIENT_SECRET +ARG AUTH_SECRET +ARG AUTH_URL +ARG NEXT_PUBLIC_BASE_URL +ARG NEXT_PUBLIC_MAPTILER_ACCESS_TOKEN +ARG SENDGRID_API_KEY +ARG SENDGRID_SENDER +COPY package*.json . +RUN npm ci --production +RUN npm i --save-dev typescript postcss ts-node COPY . . +RUN npm run db:generate RUN npm run build EXPOSE 3000 diff --git a/docker-compose.yml b/docker-compose.yml index 8645ab1..0d28e35 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,21 @@ version: "3.6" services: app: - build: . - env_file: .env + build: + context: . + dockerfile: Dockerfile + args: + DATABASE_URL: ${DATABASE_URL} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + AUTH_SECRET: ${AUTH_SECRET} + AUTH_URL: ${AUTH_URL} + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL} + NEXT_PUBLIC_MAPTILER_ACCESS_TOKEN: ${NEXT_PUBLIC_MAPTILER_ACCESS_TOKEN} + SENDGRID_API_KEY: ${SENDGRID_API_KEY} + SENDGRID_SENDER: ${SENDGRID_SENDER} + env_file: .env.production + image: computational_puzzles ports: - "3000:3000" volumes: @@ -17,7 +30,7 @@ services: image: postgres:9.6 volumes: - db_data:/var/lib/postgresql/data - env_file: .env + env_file: .env.production environment: POSTGRES_USER: computational_puzzles POSTGRES_PASSWORD: computational_puzzles # This is used to create development database, please change when migrate to production diff --git a/package.json b/package.json index defb462..118f564 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,10 @@ "lint": "next lint", "test": "jest --runInBand", "prettify": "npx prettier --write src/", - "db:push": "prisma db push" + "db:push": "prisma db push", + "db:generate": "prisma generate", + "migrate:prod": "prisma migrate deploy", + "db:seed": "ts-node prisma/seed/index.ts" }, "dependencies": { "@maptiler/geocoder": "^1.1.0", diff --git a/src/pages/puzzles/map/index.tsx b/src/pages/puzzles/map/index.tsx index 66e868b..30fb7f3 100644 --- a/src/pages/puzzles/map/index.tsx +++ b/src/pages/puzzles/map/index.tsx @@ -73,7 +73,7 @@ const PuzzleMap = ({ puzzleInstances }: PuzzleMapProps) => {
- @@ -88,13 +88,13 @@ const PuzzleMap = ({ puzzleInstances }: PuzzleMapProps) => { userMarker={userMarker} mapCenter={mapCenter} setMapCenter={setMapCenter} - /> + />}
Nearest Puzzles From Map Center:
- @@ -121,7 +121,7 @@ const PuzzleMap = ({ puzzleInstances }: PuzzleMapProps) => { ] }; })} - /> + />}
diff --git a/tsconfig.json b/tsconfig.json index 718b7ef..a2050e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "strict": false, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, - "module": "esnext", + "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, From 305437f5d49daf9395796d4b8ed7cd98993371b8 Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Mon, 18 Apr 2022 23:11:21 +0700 Subject: [PATCH 09/19] Integrate new header to profile and admin --- .../App/DisplayPuzzleInstances/index.tsx | 18 +++++++++++++++++- src/pages/admin/index.tsx | 2 +- src/pages/auth/profile/index.tsx | 3 ++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/App/DisplayPuzzleInstances/index.tsx b/src/components/App/DisplayPuzzleInstances/index.tsx index bbae03d..d53c78e 100644 --- a/src/components/App/DisplayPuzzleInstances/index.tsx +++ b/src/components/App/DisplayPuzzleInstances/index.tsx @@ -8,6 +8,7 @@ import { Button } from '../../Global'; import { QRGenerator } from '../../App'; import toast from 'react-hot-toast'; import { getLinkToPuzzleInstance } from '../../../utils/getLinkToPuzzleInstance'; +import Router from 'next/router'; type DisplayPuzzleInstancesProps = { puzzlesList: PuzzleCustom[]; @@ -26,6 +27,11 @@ const DisplayPuzzleInstances = ({ setAllPuzzleInstances(await getAllPuzzleInstances()); }; + const handleRedirectToPuzzlePage = (puzzleInstanceId) => { + const linkToPuzzlePage = getLinkToPuzzleInstance(puzzleInstanceId); + Router.push(linkToPuzzlePage); + } + useEffect(() => { getPuzzleInstances(); }, [puzzlesList]); @@ -99,7 +105,7 @@ const DisplayPuzzleInstances = ({ />
-
- {puzzleInstances && - difficultySelected[instance.puzzle.difficulty] === true - ) - .map(instance => { - return { - anchor: [instance.latitude, instance.longitude], - zoom: 16 - }; - })} - userMarker={userMarker} - mapCenter={mapCenter} - setMapCenter={setMapCenter} - />} + {puzzleInstances && ( + + difficultySelected[instance.puzzle.difficulty] === true + ) + .map(instance => { + return { + anchor: [instance.latitude, instance.longitude], + zoom: 16 + }; + })} + userMarker={userMarker} + mapCenter={mapCenter} + setMapCenter={setMapCenter} + /> + )}
Nearest Puzzles From Map Center:
- {puzzleInstances && - difficultySelected[instance.puzzle.difficulty] === true - ) - .map((instance, index) => { - return { - ...instance.puzzle, - content: [ - `Find at: ${instance.address}`, - `Hint: ${instance.hint}` - ], - buttonActions: [ - { - text: 'Solve Online', - style: 'primary', - link: `/puzzles/${instance.id}` - }, - { - text: 'View On Map', - style: 'secondary', - action: () => setMapCenterFromInstanceIndex(index) - } - ] - }; - })} - />} + {puzzleInstances && ( + + difficultySelected[instance.puzzle.difficulty] === true + ) + .map((instance, index) => { + return { + ...instance.puzzle, + content: [ + `Find at: ${instance.address}`, + `Hint: ${instance.hint}` + ], + buttonActions: [ + { + text: 'Solve Online', + style: 'primary', + link: `/puzzles/${instance.id}` + }, + { + text: 'View On Map', + style: 'secondary', + action: () => setMapCenterFromInstanceIndex(index) + } + ] + }; + })} + /> + )}
diff --git a/src/utils/difficulty.ts b/src/utils/difficulty.ts index 63f446f..06558c0 100644 --- a/src/utils/difficulty.ts +++ b/src/utils/difficulty.ts @@ -1,4 +1,4 @@ -import { Difficulty } from "@prisma/client"; +import { Difficulty } from '@prisma/client'; const difficultySentenceCase = (difficulty: Difficulty) => { return difficulty.charAt(0).toUpperCase() + difficulty.slice(1).toLowerCase(); From 4bbbc64a52ddc8551ebc53198a5a77a2e0c4cf8f Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Tue, 19 Apr 2022 20:11:13 +0700 Subject: [PATCH 13/19] Fix hash functions structure --- src/utils/password.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/utils/password.ts b/src/utils/password.ts index 399551f..e9a36dd 100644 --- a/src/utils/password.ts +++ b/src/utils/password.ts @@ -1,19 +1,21 @@ import argon2 from 'argon2'; -const hashFunction = (secret: string) => { - try { - return argon2.hash(secret, { type: argon2.argon2id }); - } catch (err) { - throw err; - } +const hashFunction = (secret: string): Promise => { + return new Promise((resolve, reject) => { + argon2 + .hash(secret, { type: argon2.argon2id }) + .then(hash => resolve(hash)) + .catch(err => reject(err)); + }); }; -const checkHash = (secret: string, hashValue: string) => { - try { - return argon2.verify(hashValue, secret); - } catch (err) { - throw err; - } +const checkHash = (secret: string, hashValue: string): Promise => { + return new Promise((resolve, _reject) => { + argon2 + .verify(hashValue, secret) + .then(res => resolve(res)) + .catch(() => resolve(false)); + }); }; export { hashFunction, checkHash }; From 2d638cf9124b2729f341c8f278aa0142d10706a2 Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Tue, 19 Apr 2022 20:27:29 +0700 Subject: [PATCH 14/19] Use regexp to convert str to bool --- src/pages/api/puzzles/instances/[instanceId]/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/api/puzzles/instances/[instanceId]/index.ts b/src/pages/api/puzzles/instances/[instanceId]/index.ts index 86ac7e0..00690de 100644 --- a/src/pages/api/puzzles/instances/[instanceId]/index.ts +++ b/src/pages/api/puzzles/instances/[instanceId]/index.ts @@ -12,7 +12,7 @@ const getPuzzleInstanceHandler = async ( verbose: string; }; - const verboseBool = verbose.toLowerCase() === 'true'; // TODO: seems weak + const verboseBool = (/true/i).test(verbose.toLowerCase()); if (!instanceId) { return res.status(400).json({ From 922657b1bb842e8f6cb2f52f35d79b46368f820c Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Tue, 3 May 2022 13:56:50 +0700 Subject: [PATCH 15/19] Calculate success rate --- Dockerfile | 1 + docker-compose.yml | 1 + package-lock.json | 2 +- package.json | 2 +- src/components/App/PuzzleInfomation/index.tsx | 39 ++++++++++++++++++- src/components/Global/Navbar/index.tsx | 2 +- src/pages/api/puzzles/{[id].tsx => [id].ts} | 0 src/pages/api/puzzles/{index.tsx => index.ts} | 0 src/pages/api/submissions/index.ts | 20 ++++++++++ src/pages/auth/login/index.tsx | 3 +- src/pages/auth/profile/index.tsx | 3 +- src/services/index.ts | 28 ++++--------- src/services/submissions.ts | 16 ++++++++ src/types/cards.ts | 4 +- src/types/radioButton.ts | 4 +- 15 files changed, 94 insertions(+), 31 deletions(-) rename src/pages/api/puzzles/{[id].tsx => [id].ts} (100%) rename src/pages/api/puzzles/{index.tsx => index.ts} (100%) create mode 100644 src/pages/api/submissions/index.ts create mode 100644 src/services/submissions.ts diff --git a/Dockerfile b/Dockerfile index c93a629..613e593 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM node:16 WORKDIR . +ARG NODE_ENV ARG DATABASE_URL ARG GOOGLE_CLIENT_ID ARG GOOGLE_CLIENT_SECRET diff --git a/docker-compose.yml b/docker-compose.yml index 0d28e35..6ea9f56 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,7 @@ services: context: . dockerfile: Dockerfile args: + NODE_ENV: ${NODE_ENV} DATABASE_URL: ${DATABASE_URL} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} diff --git a/package-lock.json b/package-lock.json index 1640ee7..4277dcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@next-auth/prisma-adapter": "^0.5.2-next.19", "@prisma/client": "^3.6.0", "@types/react": "^17.0.27", - "argon2": "^0.28.3", + "argon2": "^0.28.5", "axios": "^0.24.0", "babel-plugin-superjson-next": "^0.4.2", "next": "^12.0.7", diff --git a/package.json b/package.json index b0b64a1..e61f545 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@next-auth/prisma-adapter": "^0.5.2-next.19", "@prisma/client": "^3.6.0", "@types/react": "^17.0.27", - "argon2": "^0.28.3", + "argon2": "^0.28.5", "axios": "^0.24.0", "babel-plugin-superjson-next": "^0.4.2", "next": "^12.0.7", diff --git a/src/components/App/PuzzleInfomation/index.tsx b/src/components/App/PuzzleInfomation/index.tsx index 913b73a..8d00329 100644 --- a/src/components/App/PuzzleInfomation/index.tsx +++ b/src/components/App/PuzzleInfomation/index.tsx @@ -1,8 +1,44 @@ import * as React from 'react'; +import type { PuzzleInstance, Submission } from '@prisma/client'; import type { PuzzleCustom } from '../../../types/api/puzzles/puzzle'; import styles from './PuzzleInfomation.module.scss'; +import { getAllPuzzleInstances, getAllSubmissions } from '../../../services'; const PuzzleInfomation = ({ puzzlesList }: { puzzlesList: PuzzleCustom[] }) => { + const allPuzzleInstances = React.useRef([]); + const allSubmissions = React.useRef([]); + + // TODO: apply SWR + React.useEffect(() => { + getAllPuzzleInstances().then((res: PuzzleInstance[]) => { + allPuzzleInstances.current = res; + }); + getAllSubmissions().then((res: Submission[]) => { + allSubmissions.current = res; + }); + }, []); + + + const calculateSuccessRate = (puzzle: PuzzleCustom) => { + const puzzleId = puzzle.id; + const puzzleAnswer = puzzle.variables['answer']; + const puzzleInstances = allPuzzleInstances.current; + const submissions = allSubmissions.current; + + const filteredPuzzleInstances = puzzleInstances.filter((instance) => instance.puzzleId === puzzleId); + let countSuccess = 0; + let totalLength = 0; + filteredPuzzleInstances.forEach((puzzleInstance) => { + const filteredSubmissions = submissions.filter((submission) => submission.puzzleInstanceId === puzzleInstance.id); + totalLength += filteredSubmissions.length; + countSuccess += filteredSubmissions.filter((submission) => submission.isCorrect).length; + }); + if (totalLength === 0) { + return 'No submission recorded'; + } + return `${countSuccess / totalLength}`; + } + return (

Puzzle Infomation

@@ -20,8 +56,7 @@ const PuzzleInfomation = ({ puzzlesList }: { puzzlesList: PuzzleCustom[] }) => {
{puzzle.name}
{puzzle.difficulty}
- {/* TODO: Calculate success Rate */} - N/A + {calculateSuccessRate(puzzle)}
{/* TODO: Implement Age */} diff --git a/src/components/Global/Navbar/index.tsx b/src/components/Global/Navbar/index.tsx index 57992cc..b725996 100644 --- a/src/components/Global/Navbar/index.tsx +++ b/src/components/Global/Navbar/index.tsx @@ -48,7 +48,7 @@ const Header = ({ profilePicture }: HeaderProps) => {
{status !== 'authenticated' ? (
- +
) : (
diff --git a/src/pages/api/puzzles/[id].tsx b/src/pages/api/puzzles/[id].ts similarity index 100% rename from src/pages/api/puzzles/[id].tsx rename to src/pages/api/puzzles/[id].ts diff --git a/src/pages/api/puzzles/index.tsx b/src/pages/api/puzzles/index.ts similarity index 100% rename from src/pages/api/puzzles/index.tsx rename to src/pages/api/puzzles/index.ts diff --git a/src/pages/api/submissions/index.ts b/src/pages/api/submissions/index.ts new file mode 100644 index 0000000..c5c33d8 --- /dev/null +++ b/src/pages/api/submissions/index.ts @@ -0,0 +1,20 @@ +import { PrismaClient } from '@prisma/client'; +import { NextApiRequest, NextApiResponse } from 'next'; + +const prisma = new PrismaClient(); + +const listAllSubmissions = async ( + req: NextApiRequest, + res: NextApiResponse +) => { + try { + const puzzles = await prisma.submission.findMany(); + return res.status(200).json(puzzles); + } catch (error) { + return res.status(500).json({ + message: error.message + }); + } +}; + +export default listAllSubmissions; diff --git a/src/pages/auth/login/index.tsx b/src/pages/auth/login/index.tsx index e7c4475..d2b89fd 100644 --- a/src/pages/auth/login/index.tsx +++ b/src/pages/auth/login/index.tsx @@ -17,6 +17,7 @@ export default function LoginPage({ providers, csrfToken }) { const loginWithGoogle = () => { signIn('google').then(r => { // TODO: save session into db (?) + console.log(r); console.log('Signed in with Google'); }); }; @@ -53,7 +54,7 @@ export default function LoginPage({ providers, csrfToken }) { style={'primary'} content={'Log In'} type={'submit'} - onClick={() => {}} + onClick={() => { }} />
) : ( <> - {validAdmin && + {NavContent(true)} -
+ ); }; diff --git a/src/pages/api/puzzles/instances/[instanceId]/index.ts b/src/pages/api/puzzles/instances/[instanceId]/index.ts index 00690de..6d4aeb4 100644 --- a/src/pages/api/puzzles/instances/[instanceId]/index.ts +++ b/src/pages/api/puzzles/instances/[instanceId]/index.ts @@ -12,7 +12,7 @@ const getPuzzleInstanceHandler = async ( verbose: string; }; - const verboseBool = (/true/i).test(verbose.toLowerCase()); + const verboseBool = /true/i.test(verbose.toLowerCase()); if (!instanceId) { return res.status(400).json({ diff --git a/src/pages/auth/login/index.tsx b/src/pages/auth/login/index.tsx index d2b89fd..35ca324 100644 --- a/src/pages/auth/login/index.tsx +++ b/src/pages/auth/login/index.tsx @@ -54,7 +54,7 @@ export default function LoginPage({ providers, csrfToken }) { style={'primary'} content={'Log In'} type={'submit'} - onClick={() => { }} + onClick={() => {}} /> diff --git a/src/pages/index.tsx b/src/pages/index.tsx index a885044..43c1625 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useSession } from 'next-auth/react'; import { Header, Footer, LandingContent } from '../components/Product'; -const Home: React.FC = () => { +const Home = () => { const { data: session, status } = useSession(); if (status === 'authenticated') { From 9059514d53076f48fb4ba468db5d5cb55ed239cf Mon Sep 17 00:00:00 2001 From: Kiet Phan Date: Wed, 4 May 2022 21:32:25 +0700 Subject: [PATCH 18/19] Update document for production --- README.md | 18 ++++++++++++++++-- src/components/App/PuzzleInfomation/index.tsx | 6 +++--- src/pages/admin/index.tsx | 1 - 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7967ffd..5c980b1 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,23 @@ npx prisma studio ``` ## Start production server +Create a file named `.env.production`: +``` +NODE_ENV=production + +DATABASE_URL=postgresql://computational_puzzles:computational_puzzles@db:5432/mydb?schema=public&connect_timeout=300 +GOOGLE_CLIENT_ID=10889722286-8uek1esq4uicv31an6tehi60c7ev5lvp.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=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 +SECRET=secret +AUTH_SECRET=secret +AUTH_URL=http://localhost:3000 + +NEXT_PUBLIC_BASE_URL=http://localhost:3000 +NEXT_PUBLIC_MAPTILER_ACCESS_TOKEN=uTyRuREEmko5kblSVwhb +``` +Then, run the following command ```bash -docker-compose build -docker-compose up +docker-compose up -- build ``` ## For testing diff --git a/src/components/App/PuzzleInfomation/index.tsx b/src/components/App/PuzzleInfomation/index.tsx index 5e9829c..4a62842 100644 --- a/src/components/App/PuzzleInfomation/index.tsx +++ b/src/components/App/PuzzleInfomation/index.tsx @@ -33,8 +33,8 @@ const PuzzleInfomation = ({ puzzlesList }: { puzzlesList: PuzzleCustom[] }) => { filteredPuzzleInstances.forEach(puzzleInstance => { const filteredSubmissions = submissions ? submissions.filter( - submission => submission.puzzleInstanceId === puzzleInstance.id - ) + submission => submission.puzzleInstanceId === puzzleInstance.id + ) : []; console.log(puzzle.name, filteredSubmissions); totalLength += filteredSubmissions.length; @@ -46,7 +46,7 @@ const PuzzleInfomation = ({ puzzlesList }: { puzzlesList: PuzzleCustom[] }) => { if (totalLength === 0) { return 'No submission recorded'; } - return `${(countSuccess / totalLength) * 100}%`; + return `${Math.round((countSuccess / totalLength) * 10000) / 100}%`; // Rounding float to 2 decimal place }; return ( diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index db53d71..2f9b06c 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -19,7 +19,6 @@ const Admin = ({ puzzlesList }: { puzzlesList: PuzzleCustom[] }) => { <>
-

Admin Dashboard

Date: Tue, 10 May 2022 00:17:52 +0700 Subject: [PATCH 19/19] Name db container --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 96d8dfd..58381be 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,7 @@ services: db: image: postgres:9.6 + container_name: computational_puzzle_db volumes: - db_data:/var/lib/postgresql/data env_file: .env.production