Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quiz AI hippos 2 #115

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9fc561d
Optionbuttons, questions and progression
JosefinRobertsson Apr 12, 2023
a4c10a4
added styled_components folder and a buttons-file
JosefinRobertsson Apr 13, 2023
bc202a5
Added hints, Hintcomponent and cond. rendering
JosefinRobertsson Apr 13, 2023
0eb02d9
Added button disabler to avoid multiple clicks
JosefinRobertsson Apr 13, 2023
6523c4d
Office day results
JosefinRobertsson Apr 14, 2023
0433ab9
Color change for right and wrong answers
JosefinRobertsson Apr 15, 2023
90f0207
changed fonts on optionbtn & animation on startbtn
JosefinRobertsson Apr 15, 2023
115c202
animation update startbutton
JosefinRobertsson Apr 15, 2023
c80c088
summary
cpz-crafts Apr 15, 2023
cda6f3c
mobile responsive check
cpz-crafts Apr 15, 2023
1764a58
padding
cpz-crafts Apr 15, 2023
d29ed8d
added hints used to show in summary
Apr 16, 2023
ecc7595
changed false to null on line 145 CQ
JosefinRobertsson Apr 16, 2023
b20e3a3
Merge branch 'master' of https://github.com/JosefinRobertsson/project…
JosefinRobertsson Apr 16, 2023
fd31c95
changed eol
JosefinRobertsson Apr 16, 2023
6c28e67
Merge remote-tracking branch 'origin/cpz-styling'
JosefinRobertsson Apr 16, 2023
bf6fd21
added ogtags and updated styling for responsivness
Apr 16, 2023
ee2fda2
adjustment to images, change to hero image on phone, small adjustment…
Apr 16, 2023
58daea2
removed unneccesary comments
Apr 16, 2023
4c8604b
prevent video playing on mobile after restart
JosefinRobertsson Apr 16, 2023
f307196
restartbtn visibility + tiny bit more glitch
JosefinRobertsson Apr 16, 2023
7914f89
Update README.md
JosefinRobertsson Apr 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30,241 changes: 0 additions & 30,241 deletions package-lock.json

This file was deleted.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.2",
"react-scripts": "^5.0.1"
"react-scripts": "^5.0.1",
"styled-components": "^5.3.9",
"tsparticles": "^2.9.3"
},
"scripts": {
"start": "react-scripts start",
Expand Down
6 changes: 5 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Orbitron:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sarpanch:wght@400;700&display=swap">
<meta property ="og:title" content="Technigo React Quiz App by Carol, Emma, Josefin & Malin"/>
<meta property ="og:description" content="A website displaying a quiz about AI."/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand All @@ -13,7 +17,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Technigo React App</title>
<title>A Quiz about AI</title>
</head>

<body>
Expand Down
29 changes: 23 additions & 6 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import React, { useState } from 'react';
import { Provider } from 'react-redux';
import GlobalStyle from 'components/styled_components/globalStyles';
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { quiz } from 'reducers/quiz';

import { CurrentQuestion } from 'components/CurrentQuestion';
import { LandingPage } from 'components/LandingPage';

const reducer = combineReducers({
quiz: quiz.reducer
Expand All @@ -12,9 +13,25 @@ const reducer = combineReducers({
const store = configureStore({ reducer });

export const App = () => {
const [startQuiz, setStartQuiz] = useState(false);

const handleStartQuiz = () => {
setStartQuiz(true);
};

return (
<Provider store={store}>
<CurrentQuestion />
</Provider>
<>
<GlobalStyle />
<Provider store={store}>
{/* When startQuiz is false, it renders the LandingPage component.
When startQuiz becomes true (after the user clicks the "Start Quiz" button),
the CurrentQuestion component is rendered instead. */}
{!startQuiz ? (
<LandingPage onStart={handleStartQuiz} />
) : (
<CurrentQuestion />
)}
</Provider>
</>
);
}
};
206 changes: 197 additions & 9 deletions src/components/CurrentQuestion.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,204 @@
import React from 'react'
import { useSelector } from 'react-redux'
/* eslint-disable no-undef */
import React, { useState, useEffect } from 'react';
import styled, { css } from 'styled-components';
import { useSelector, useDispatch } from 'react-redux';
import { quiz } from 'reducers/quiz';
import { HintButton } from './styled_components/buttons.js'
import { Progressbar } from './styled_components/progressbar.js';
import { QuizSummary } from './QuizSummary'
import Hint from './Hint.js';

const QuestionWrapper = styled.div`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 80vw;
background-color: gray;
border: 3px solid black;
@media (min-width: 744px) {
width: 40vw;
}
`;

const QuestionContainer = styled.div`
position: relative;
display: flex;
background-color: rgba(255, 255, 255, .15);
backdrop-filter: blur(9px);
align-items: center;
justify-content: center;
text-align: center;
width: 90%;
height: 40vh;
padding: 2rem;
margin-bottom: 2rem;
margin-top: 9rem;
z-index: 2;
border-radius: 5px;
`;

const OptionsContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
font-family: "Sarpanch";
`
const OptionButton = styled.button`
width: 90%;
height: 4rem;
border-radius: 5px;
margin-bottom: 1rem;
background-color: rgb(151,171,169);
color: white;
font-family: "Sarpanch";
font-size: 20px;
cursor: pointer;
${({ isCorrect }) => isCorrect && css`
background-color: #99a771;
border: 1px solid white;
font-size: 23px;
font-weight: bold;
`}
:disabled {
${({ isCorrect }) => !isCorrect && css`
background-color: #834f39;
opacity: 0.5;
`}
}
`

const ImgQuestion = styled.img`
position: absolute;
display: flex;
flex-direction: column;
width: 40vw;
height: 40vh;
z-index: 1;
`;

const HintContainer = styled.div`
width: fit-content;
background-color: white;
border: 2px solid orange;
padding: 1rem;
font-family: "Sarpanch";
`;

/*
const clip = keyframes`
0% {
clip-path: polygon(
0 100%,
100% 100%,
100% 120%,
0 120%
);
}

100% {
clip-path: polygon(
0 -20%,
100% -20%,
100% 0%,
0 0
);
}
`;
const GlitchHeading = styled.h1`
position: relative;
color: #fff;

${[...Array(10)].map((_, i) => `
&:before:nth-child(${i + 1}) {
content: '${i === 0 ? '' : 'Question: '}';
position: absolute;
top: 0;
left: 0;
animation:
${clip} 3000ms ${(i + 1) * -300}ms linear infinite,
${glitch(i + 1)} 500ms ${(Math.random() * 1000) * -1}ms linear infinite;
}
`).join('')}
`;
*/

export const CurrentQuestion = () => {
const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex])
const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex]);
const dispatch = useDispatch();
const [showHint, setShowHint] = useState(false);
const [wasClicked, setWasClicked] = useState(false);
const [selectedAnswer, setSelectedAnswer] = useState(null);
const [answerCorrect, setAnswerCorrect] = useState(null);
const [showCorrectAnswer, setShowCorrectAnswer] = useState(false);

const quizOver = useSelector((state) => state.quiz.quizOver)
console.log(answerCorrect, showCorrectAnswer)

useEffect(() => {
setShowHint(false);
setWasClicked(false);
setSelectedAnswer(null);
setAnswerCorrect(null);
setShowCorrectAnswer(null);
}, [question]); // Sets state to false or null every time the question changes

const handleAnswerClick = (answerIndex) => {
const isCorrect = question.correctAnswerIndex === answerIndex;
dispatch(quiz.actions.submitAnswer({ questionId: question.id, answerIndex }));
// Submit answer to the store
setSelectedAnswer(answerIndex);
setAnswerCorrect(isCorrect);
setWasClicked(true);
setShowCorrectAnswer(true);

setTimeout(() => {
dispatch(quiz.actions.goToNextQuestion());
}, 1500); // Delays the rendering of the next question after a button has been clicked
};

if (quizOver) {
return <QuizSummary />
}

const handleHintClick = () => {
setShowHint(!showHint);
dispatch(quiz.actions.useHint())
}

if (!question) {
return <h1>Oh no! I could not find the current question!</h1>
return <h1>Oh no! I could not find the current question!</h1>;
}

return (
<div>
<h1>Question: {question.questionText}</h1>
</div>
)
}
<>
<ImgQuestion src={question.imgUrl} />
<QuestionWrapper>
<QuestionContainer>
<h1>{question.questionText}</h1>
</QuestionContainer>
<OptionsContainer>
{question.options.map((optionText, optionIndex) => (
<OptionButton
key={optionText}
onClick={() => handleAnswerClick(optionIndex)}
disabled={wasClicked}
isCorrect={(selectedAnswer === optionIndex && answerCorrect)
|| (optionIndex === question.correctAnswerIndex && showCorrectAnswer)}
// Shows correct answer after any click, correct or not
isIncorrect={selectedAnswer === optionIndex && !answerCorrect}>
{optionText}
</OptionButton>
))}
</OptionsContainer>
<HintContainer>
<HintButton disabled={showHint} onClick={handleHintClick}>{showHint ? 'Time to make a guess' : 'Would you like a hint?'}</HintButton>
{showHint && <Hint question={question} />}
</HintContainer>
<Progressbar totalQuestions={5} />
</QuestionWrapper>
</>
);
};
9 changes: 9 additions & 0 deletions src/components/Hint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const Hint = ({ question }) => {
return (
<p>{question.hint}</p>
);
};

export default Hint;
18 changes: 18 additions & 0 deletions src/components/LandingPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import styled from 'styled-components';
import { StartButton } from './styled_components/buttons';
import TestImg from '../images/testimg.jpeg'

const LandingHeadline = styled.h1`
color: black;`

export const LandingPage = ({ onStart }) => {
return (
<div>
<img src={TestImg} alt="something" />
<LandingHeadline>Welcome to the last quiz</LandingHeadline>
<p>Test your knowledge about AI</p>
<StartButton onClick={onStart}>START QUIZ</StartButton>
</div>
);
};
40 changes: 40 additions & 0 deletions src/components/QuizSummary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import styled from 'styled-components'
import { quiz } from 'reducers/quiz'
import { RestartButton } from './styled_components/buttons.js'

const SummaryHeadline = styled.h1`
color: black;
`

export const QuizSummary = () => {
const answers = useSelector((state) => state.quiz.answers);
const dispatch = useDispatch();

const handleRestart = () => {
dispatch(quiz.actions.restart())
};
/* Calculate the number of correct answers : correctAnswers is calculated by
filtering the answers array to only include correct answers */
const correctAnswers = answers.filter((answer) => answer.isCorrect).length;
const hintUsed = useSelector((state) => state.quiz.hintsUsed)

let message;
if (correctAnswers === answers.length) {
message = 'Wow impressive, you got everything right about AI!';
} else if (correctAnswers === 0) {
message = 'sorry no correct answers, you are doomed!! 🫠'
} else {
message = `You answered ${correctAnswers} out of ${answers.length} questions correctly.`
}

return (
<div>
<SummaryHeadline>Quiz Summary</SummaryHeadline>
<p>{message}</p>
<p>Hints used: {hintUsed}</p>
<RestartButton onClick={handleRestart}>Restart Quiz</RestartButton>
</div>
)
}
Loading