├── .eslintrc.json ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── _document.jsx ├── api │ └── question.js └── index.jsx ├── postcss.config.js ├── public ├── favicon.ico └── vercel.svg ├── styles └── globals.css └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | node_modules 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Galih Sukristyan Saputra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Simple NextJS Quiz App 4 | 5 |
6 | 7 |
8 | 9 | [![All Contributors](https://img.shields.io/github/contributors/masgalih320/quizapp-nextjs)](https://github.com/masgalih320/quizapp-nextjs/graphs/contributors) 10 | ![GitHub last commit](https://img.shields.io/github/last-commit/masgalih320/quizapp-nextjs.svg) 11 | ![GitHub repo size in bytes](https://img.shields.io/github/languages/code-size/masgalih320/quizapp-nextjs) 12 | [![License](https://img.shields.io/github/license/masgalih320/quizapp-nextjs)](LICENSE) 13 | 14 |
15 | 16 |

17 | Simple NextJS Quiz App made with NextJS and TailwindCSS 18 |

19 | 20 | ## Features 21 | 22 | - Timer 23 | - Question bank stored on API endpoint on `pages/api/question.js` file and easy to modify the question 24 | - Game Over screen 25 | 26 | ## Future update 27 | 28 | - Before play screen (user can login/input username here) 29 | - Choose quiz room screen (public quiz) 30 | - Quiz room (room code for private quiz) 31 | - Responsive page 32 | - More interactive 33 | 34 | ## Installation Guide 35 | 36 | ### Build setup 37 | 38 | ```bash 39 | # clone the repository 40 | $ git clone https://github.com/masgalih320/quizapp-nextjs quiz 41 | 42 | # navigate to the folder 43 | $ cd quiz 44 | ``` 45 | 46 | After clone the repository and navigate to the folder, you can use few commands below 47 | 48 | ```bash 49 | # install dependencies 50 | $ npm install 51 | 52 | # serve with hot reload at localhost:3000 53 | $ npm run dev 54 | 55 | # build for production and launch server 56 | $ npm run build 57 | $ npm run start 58 | ``` 59 | 60 | For detailed explanation on how things work, checkout [Next.JS docs](https://nextjs.org) 61 | 62 | ## Contributing 63 | 64 | See contributing Guide [here](./CONTRIBUTING.md) 65 | 66 | ## License 67 | 68 | Simple NextJS Quiz App is under MIT License 69 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quiz", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "13.0.0", 13 | "react": "18.2.0", 14 | "react-dom": "18.2.0" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^10.4.12", 18 | "eslint": "8.26.0", 19 | "eslint-config-next": "13.0.0", 20 | "postcss": "^8.4.18", 21 | "tailwindcss": "^3.2.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /pages/_document.jsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | Simple Quiz App with NextJS 8 | 9 | 10 |
11 | 12 | 13 | 14 | ) 15 | } -------------------------------------------------------------------------------- /pages/api/question.js: -------------------------------------------------------------------------------- 1 | // Define API routes for getting question bank 2 | export default function handler(req, res) { 3 | return res.status(200).json({ 4 | status: 200, 5 | msg: "success", 6 | data: [ 7 | { 8 | time: 10000, 9 | question: "Siapakah presiden Negara Republik Indonesia ke-1?", 10 | answer: "Ir. Soekarno", 11 | choices: ["WR. Soepratman", "Soeharto", "Puan Maharani", "Ir. Soekarno"], 12 | }, 13 | { 14 | time: 5000, 15 | question: "Kerusuhan Banjarmasin terjadi pada tahun berapa?", 16 | answer: 1997, 17 | choices: [1997, 2002, 2013, 1945], 18 | }, 19 | { 20 | time: 20000, 21 | question: "Kepanjangan DPR adalah?", 22 | answer: "Dewan Perwakilan Rakyat", 23 | choices: ["Dewan Perwakilan Daerah", "Dewan Perwakilan Rakyat", "Dewan Pribumi Rahasia", "Dewan Pengkhianat Rakyat"], 24 | }, 25 | { 26 | time: 10000, 27 | question: "Kepanjangan TNI adalah?", 28 | answer: "Tentara Nasional Indonesia", 29 | choices: ["Tentara Nasional Indonesia", "Tau Nama Indonesia", "Takutnya Negara Indonesia", "Tinggal Nama Ini"], 30 | }, 31 | ], 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /pages/index.jsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router" 2 | import { useEffect, useState } from "react" 3 | 4 | // test 5 | export default function Home({ question }) { 6 | const router = useRouter() 7 | const [isOver, setIsOver] = useState(false) 8 | const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0) 9 | const [currentQuestionAnswer, setCurrentQuestionAnswer] = useState(question[currentQuestionIndex] ? question[currentQuestionIndex].answer : "") 10 | const [correctAnswerTotal, setCorrectAnswerTotal] = useState(0) 11 | const [incorrectAnswerTotal, setIncorrectAnswerTotal] = useState(0) 12 | const [timer, setTimer] = useState(100) 13 | 14 | useEffect(() => { 15 | if (!isOver) { 16 | const interval = setInterval(() => { 17 | if (timer > 0) setTimer((timer) => Math.floor(timer - Math.floor(question[currentQuestionIndex].time / 1000))) 18 | }, 1000) 19 | 20 | if (timer == 0 && question.length - 1 == currentQuestionIndex) setIsOver(true) 21 | if (timer == 0 && isOver == false) setCurrentQuestionIndex(currentQuestionIndex + 1) 22 | if (timer == 0 && isOver == false) setTimer(100) 23 | if (timer == 0 && isOver == false) setIncorrectAnswerTotal(incorrectAnswerTotal + 1) 24 | 25 | return () => clearInterval(interval) 26 | } else { 27 | setTimer(0) 28 | } 29 | }, [timer]) 30 | 31 | function handleClick(ans) { 32 | setCurrentQuestionIndex((currentQuestionIndex) => { 33 | ans === currentQuestionAnswer ? setCorrectAnswerTotal(correctAnswerTotal + 1) : setIncorrectAnswerTotal(incorrectAnswerTotal + 1) 34 | if (timer !== 0 && isOver == false && question.length - 1 == currentQuestionIndex) return setIsOver(true) 35 | if (timer !== 0 && isOver == false) setTimer(100) 36 | if (timer !== 0 && isOver == false) setCurrentQuestionAnswer(question[currentQuestionIndex + 1].answer) 37 | if (timer !== 0 && isOver == false) return currentQuestionIndex + 1 38 | }) 39 | } 40 | 41 | return ( 42 |
43 | {isOver ?
44 |
45 |
46 |

Your score

47 |
48 |
49 |

{correctAnswerTotal} / {question.length}

50 |
Incorrect Answer: {incorrectAnswerTotal}
51 | 52 |
53 |
54 |
55 |
56 |
: ""} 57 | 58 |
59 |
60 |
61 |

62 |
Question: {question.length}
63 |
Correct: {correctAnswerTotal}
64 |
Incorrect: {incorrectAnswerTotal}
65 |

66 |
{question.length - 1 >= currentQuestionIndex ? question[currentQuestionIndex].question : ""}
67 |
68 | 69 |
70 | {question.length - 1 >= currentQuestionIndex ? question[currentQuestionIndex].choices.map((seg) => { 71 | return ( 72 |
handleClick(seg)} className="p-4 bg-blue-400 border-blue-500 hover:bg-blue-600 hover:border-blue-600 duration-300 border-2 h-80 w-full rounded-lg cursor-pointer flex items-center justify-center"> 73 |
{seg}
74 |
75 | ) 76 | }) : ""} 77 |
78 |
79 | ) 80 | } 81 | 82 | export async function getStaticProps() { 83 | const getQuestion = await fetch(`http://localhost:3000/api/question`) 84 | const question = await getQuestion.json() 85 | return { 86 | props: { 87 | question: question.data 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukristyan/quizapp-nextjs/7eca8e12e50fd2b796565dbc46fbfa41bd96efdd/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | --------------------------------------------------------------------------------