Skip to content

Commit fa75715

Browse files
authored
quiz app with ReactJS
1 parent 2bceaf2 commit fa75715

File tree

7 files changed

+372
-0
lines changed

7 files changed

+372
-0
lines changed

quiz-app/app.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from "react";
2+
import SetupQuiz from "./setupQuiz";
3+
4+
const App = () => {
5+
return (
6+
<div className="quiz-container">
7+
<SetupQuiz />
8+
</div>
9+
);
10+
};
11+
12+
export default App;

quiz-app/assets/loading.gif

55.8 KB
Loading

quiz-app/index.css

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
:root {
2+
--bg-color: #f1f5f8;
3+
--primary-color: #333333;
4+
--secondary-color: #30475f;
5+
--answer-btn-bg: #5e96db;
6+
--answer-btn-hover-bg: #1d61bb;
7+
--transition: 0.5s;
8+
--primary-btn: #fccb43;
9+
--success-color: #25bb32;
10+
--box-shadow: 1px 1px 8px #dedede;
11+
--radius: 5px;
12+
--letter-spacing: 1.4px;
13+
--error-color: red;
14+
}
15+
16+
body {
17+
background: var(--bg-color);
18+
color: var(--primary-color);
19+
}
20+
21+
.quiz-container {
22+
display: flex;
23+
flex-direction: column;
24+
justify-content: center;
25+
align-items: center;
26+
height: 100vh;
27+
}
28+
29+
.container {
30+
text-align: center;
31+
}
32+
33+
.setup-card {
34+
background: white;
35+
box-shadow: var(--box-shadow);
36+
border-radius: var(--radius);
37+
border: 1px solid white;
38+
width: 500px;
39+
margin: auto;
40+
text-align: left;
41+
padding: 30px;
42+
}
43+
44+
.form-section {
45+
padding-top: 12px;
46+
margin-top: 12px;
47+
color: var(--secondary-color);
48+
font-weight: bold;
49+
}
50+
51+
#amount {
52+
background: #efefef;
53+
border: 1px solid #efefef;
54+
padding: 3px 12px;
55+
border-radius: var(--radius);
56+
width: 100%;
57+
}
58+
59+
select {
60+
width: 100%;
61+
background: #efefef;
62+
border-radius: var(--radius);
63+
padding: 1.5px 8px;
64+
border-color: transparent;
65+
}
66+
67+
.start-btn {
68+
width: 100%;
69+
background: var(--primary-btn);
70+
border: 1px solid var(--primary-btn);
71+
border-radius: var(--radius);
72+
padding: 3px;
73+
margin-top: 20px;
74+
font-weight: 500;
75+
}
76+
77+
.error-txt {
78+
color: var(--error-color);
79+
text-align: center;
80+
margin-top: 30px;
81+
}
82+
83+
.question-text {
84+
font-weight: bold;
85+
letter-spacing: (var(--letter-spacing));
86+
font-size: 24px;
87+
margin-top: -10px;
88+
}
89+
90+
.modal-box {
91+
background: white;
92+
text-align: center;
93+
border-radius: var(--radius);
94+
padding: 30px;
95+
margin: 20px;
96+
}
97+
98+
.modal-background {
99+
background: rgba(0, 0, 0, 0.4);
100+
width: 100%;
101+
position: fixed;
102+
z-index: 99999;
103+
height: 100%;
104+
display: flex;
105+
justify-content: center;
106+
align-items: center;
107+
}
108+
109+
.option {
110+
background: var(--answer-btn-bg);
111+
border: 1px solid var(--answer-btn-bg);
112+
width: 100%;
113+
border-radius: 2px;
114+
margin-top: 20px;
115+
padding: 10px;
116+
transition: var(--transition);
117+
color: white;
118+
font-weight: bold;
119+
}
120+
121+
.option:hover {
122+
background: var(--answer-btn-hover-bg);
123+
border: 1px solid var(--answer-btn-hover-bg);
124+
}
125+
126+
.correct-answers {
127+
margin-top: -10px;
128+
color: var(--success-color);
129+
text-align: right;
130+
}
131+
132+
@media only screen and (max-width: 768px) {
133+
.setup-card {
134+
width: 100%;
135+
}
136+
.question-text {
137+
font-size: 20px;
138+
}
139+
}

quiz-app/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
import App from "./app";
4+
import "./index.css";
5+
6+
ReactDOM.render(
7+
<React.StrictMode>
8+
<App />
9+
</React.StrictMode>,
10+
document.getElementById("root")
11+
);

quiz-app/modal.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
3+
const Modal = (props) => {
4+
const { trueCount, setIsStart, questionLength } = props;
5+
6+
return (
7+
<div className="modal-background">
8+
<div className="modal-box">
9+
<h1>Congrats!</h1>
10+
<p className="lead">
11+
You answered {Math.floor((trueCount * 100) / questionLength)}% of
12+
questions correctly
13+
</p>
14+
<button className="start-btn" onClick={() => setIsStart(false)}>
15+
Play Again!
16+
</button>
17+
</div>
18+
</div>
19+
);
20+
};
21+
22+
export default Modal;
23+
24+
//x = (trueCount * 100) / questionLength;

quiz-app/quiz.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React, { useState, useEffect } from "react";
2+
import Modal from "./modal";
3+
import ReactMarkdown from "react-markdown";
4+
5+
const Quiz = (props) => {
6+
const { questions, setIsStart } = props;
7+
const [index, setIndex] = useState(0);
8+
const [isModal, setIsModal] = useState(false);
9+
const [trueCount, setTrueCount] = useState(0);
10+
const [current, setCurrent] = useState({});
11+
const [options, setOptions] = useState([]);
12+
13+
useEffect(() => {
14+
if (index > questions.length - 1) {
15+
setIndex(questions.length - 1);
16+
setOptions([]);
17+
setIsModal(true);
18+
} else {
19+
setCurrent(questions[index]);
20+
21+
let range = questions[index].type === "multiple" ? 4 : 2;
22+
let rand = Math.floor(Math.random() * range);
23+
let temp = questions[index].incorrect_answers;
24+
temp.splice(rand, 0, questions[index].correct_answer);
25+
setOptions(temp);
26+
}
27+
}, [index]);
28+
29+
const answer = (e) => {
30+
if (e.target.value === questions[index].correct_answer) {
31+
setTrueCount(trueCount + 1);
32+
}
33+
setIndex(index + 1);
34+
};
35+
36+
return (
37+
<>
38+
{isModal ? (
39+
<Modal
40+
trueCount={trueCount}
41+
setIsStart={setIsStart}
42+
questionLength={questions.length}
43+
/>
44+
) : (
45+
<div className="setup-card">
46+
<p className="correct-answers">
47+
Correct Answers: {trueCount} of {questions.length}
48+
</p>
49+
<ReactMarkdown className="question-text">
50+
{current.question}
51+
</ReactMarkdown>
52+
{options.map((option, index) => {
53+
return (
54+
<button
55+
className="option"
56+
key={index}
57+
value={option}
58+
onClick={(e) => answer(e)}
59+
>
60+
{option}
61+
</button>
62+
);
63+
})}
64+
</div>
65+
)}
66+
</>
67+
);
68+
};
69+
70+
export default Quiz;

quiz-app/setupQuiz.js

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React, { useEffect, useState } from "react";
2+
import Axios from "axios";
3+
import Loading from "./assets/loading.gif";
4+
import Quiz from "./quiz";
5+
6+
const SetupQuiz = () => {
7+
const [amount, setAmount] = useState(5);
8+
const [difficulty, setDifficulty] = useState("easy");
9+
const [category, setCategory] = useState(21);
10+
const [isStart, setIsStart] = useState(false);
11+
const [isLoading, setIsLoading] = useState(false);
12+
const [isError, setIsError] = useState(false);
13+
const [questions, setQuestions] = useState([]);
14+
const BASE_URL = `https://opentdb.com/api.php?amount=${amount}&difficulty=${difficulty}&category=${category}`;
15+
16+
useEffect(() => {
17+
if (amount > 50) {
18+
setAmount(50);
19+
} else if (amount < 1) {
20+
setAmount(1);
21+
}
22+
}, [amount]);
23+
24+
const startQuiz = () => {
25+
setIsLoading(true);
26+
Axios.get(BASE_URL)
27+
.then((resp) => resp.data.results)
28+
.then((data) => {
29+
if (data.length < 1) {
30+
setIsStart(false);
31+
setIsLoading(false);
32+
setIsError(true);
33+
} else {
34+
setQuestions(data);
35+
setIsLoading(false);
36+
setIsError(false);
37+
setIsStart(true);
38+
}
39+
})
40+
.catch((err) => {
41+
setIsError(true);
42+
setIsStart(false);
43+
});
44+
};
45+
46+
if (isLoading) {
47+
return (
48+
<>
49+
<img src={Loading} alt="Loading spinner is spinning." />
50+
</>
51+
);
52+
}
53+
54+
if (!isStart) {
55+
return (
56+
<div className="container">
57+
<div className="setup-card">
58+
<h1 style={{ fontWeight: "bold" }}>Setup Quiz</h1>
59+
<form onSubmit={(e) => e.preventDefault()}>
60+
<section className="form-section">
61+
<label htmlFor="amount">Number Of Questions</label>
62+
<br />
63+
<input
64+
type="number"
65+
name="amount"
66+
id="amount"
67+
value={amount}
68+
onChange={(e) => setAmount(e.target.value)}
69+
max="50"
70+
min="1"
71+
/>
72+
</section>
73+
<section className="form-section">
74+
<label htmlFor="category">Select Category</label>
75+
<br />
76+
<select
77+
name="category"
78+
id="category"
79+
value={category}
80+
onChange={(e) => setCategory(e.target.value)}
81+
>
82+
<option value="21">Sport</option>
83+
<option value="23">History</option>
84+
<option value="24">Politics</option>
85+
</select>
86+
</section>
87+
<section className="form-section">
88+
<label htmlFor="difficulty">Select Difficulty</label>
89+
<br />
90+
<select
91+
name="difficulty"
92+
id="difficulty"
93+
value={difficulty}
94+
onChange={(e) => setDifficulty(e.target.value)}
95+
>
96+
<option value="easy">Easy</option>
97+
<option value="normal">Normal</option>
98+
<option value="hard">Hard</option>
99+
</select>
100+
</section>
101+
<section className="form-section">
102+
<button className="start-btn" onClick={() => startQuiz()}>
103+
Start Quiz
104+
</button>
105+
{isError && <p className="error-txt">An Error Occured.</p>}
106+
</section>
107+
</form>
108+
</div>
109+
</div>
110+
);
111+
}
112+
113+
return <Quiz questions={questions} setIsStart={setIsStart} />;
114+
};
115+
116+
export default SetupQuiz;

0 commit comments

Comments
 (0)