├── src ├── index.css ├── main.jsx ├── assets │ └── react.svg └── App.jsx ├── vite.config.js ├── .gitignore ├── index.html ├── README.md ├── package.json ├── eslint.config.js └── public └── vite.svg /src/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | import App from './App.jsx'; 3 | import './index.css'; 4 | import 'bootstrap/dist/css/bootstrap.min.css'; 5 | 6 | createRoot(document.getElementById('root')).render( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Quiz 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-quiz-app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emotion/react": "^11.13.3", 14 | "@emotion/styled": "^11.13.0", 15 | "@mui/icons-material": "^6.1.0", 16 | "@mui/material": "^6.1.0", 17 | "@mui/styled-engine-sc": "^6.1.0", 18 | "axios": "^1.7.7", 19 | "bootstrap": "^5.3.3", 20 | "react": "^18.3.1", 21 | "react-dom": "^18.3.1", 22 | "styled-components": "^6.1.13" 23 | }, 24 | "devDependencies": { 25 | "@eslint/js": "^9.9.0", 26 | "@types/react": "^18.3.3", 27 | "@types/react-dom": "^18.3.0", 28 | "@vitejs/plugin-react": "^4.3.1", 29 | "eslint": "^9.9.0", 30 | "eslint-plugin-react": "^7.35.0", 31 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 32 | "eslint-plugin-react-refresh": "^0.4.9", 33 | "globals": "^15.9.0", 34 | "vite": "^5.4.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Button, 4 | Typography, 5 | CircularProgress, 6 | Paper, 7 | Radio, 8 | RadioGroup, 9 | FormControlLabel, 10 | FormControl, 11 | } from "@mui/material"; 12 | import axios from "axios"; 13 | import React, { useEffect, useState } from "react"; 14 | 15 | function App() { 16 | const [questions, setQuestions] = useState([]); 17 | const [questionState, setQuestionState] = useState(0); 18 | const [selectedAnswer, setSelectedAnswer] = useState(null); 19 | const [shuffledAnswers, setShuffledAnswers] = useState([]); 20 | const [isQuizComplete, setIsQuizComplete] = useState(false); 21 | const [score, setScore] = useState(0); 22 | const [loading, setLoading] = useState(true); 23 | 24 | 25 | useEffect(() => { 26 | axios 27 | .get("https://the-trivia-api.com/v2/questions") 28 | .then((res) => { 29 | setQuestions(res.data); 30 | setLoading(false); 31 | shuffleAnswers(res.data[0]); // Shuffle answers for the first question 32 | }) 33 | .catch((err) => { 34 | console.log(err); 35 | setLoading(false); 36 | }); 37 | }, []); 38 | 39 | function shuffleAnswers(question) { 40 | const answers = [ 41 | ...question.incorrectAnswers, 42 | question.correctAnswer, 43 | ]; 44 | for (let i = answers.length - 1; i > 0; i--) { 45 | const j = Math.floor(Math.random() * (i + 1)); 46 | [answers[i], answers[j]] = [answers[j], answers[i]]; 47 | } 48 | setShuffledAnswers(answers); // Set shuffled answers in the state 49 | } 50 | 51 | const handleNextQuestion = () => { 52 | if (!selectedAnswer) { 53 | alert("Please select an answer!"); 54 | return; 55 | } 56 | 57 | if (selectedAnswer === questions[questionState].correctAnswer) { 58 | setScore(score + 1); 59 | } 60 | 61 | if (questionState < questions.length - 1) { 62 | setQuestionState(questionState + 1); 63 | setSelectedAnswer(null); 64 | shuffleAnswers(questions[questionState + 1]); // Shuffle answers for the next question 65 | } else { 66 | setIsQuizComplete(true); 67 | } 68 | }; 69 | 70 | const renderQuestion = () => { 71 | const currentQuestion = questions[questionState]; 72 | 73 | return ( 74 | 87 | 88 | Question {questionState + 1} of {questions.length} 89 | 90 | 91 | {currentQuestion.question.text} 92 | 93 | 94 | 95 | setSelectedAnswer(e.target.value)} 99 | > 100 | {shuffledAnswers.map((answer, index) => ( 101 | } 105 | label={answer} 106 | style={{ marginBottom: "10px" }} 107 | /> 108 | ))} 109 | 110 | 111 | 112 | 120 | 121 | ); 122 | }; 123 | 124 | const renderQuizResult = () => ( 125 | 138 | 139 | Quiz Completed! 140 | 141 | 142 | Your score: {score} / {questions.length} 143 | 144 | 152 | 153 | ); 154 | 155 | return ( 156 | 157 | 158 | Quiz App 159 | 160 | 161 | {loading && } 162 | 163 | {!loading && questions.length > 0 && !isQuizComplete && renderQuestion()} 164 | 165 | {isQuizComplete && renderQuizResult()} 166 | 167 | ); 168 | } 169 | 170 | export default App; 171 | --------------------------------------------------------------------------------