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

Created by Vera Sjunesson Sammy Olsson Johanna Leonsson and Jonas Jakobson #120

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Redux quiz group project

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
The assignment was to to build a quiz game using Redux. A multiple-choice quiz, with our own questions and a bunch of possible answers to present to the users.
This was a group project made by [Jonas](https://github.com/jonsjak), [Johanna](https://github.com/JohLeo), [Vera](https://github.com/Vera-Sjunnesson) & [Sammy](https://github.com/sammyolsson)

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
We used Figma as a project planner, to list tasks and sketch design.
Regarding coding we went with the pair-programming approach with mobile first in mind.
We focused on the functionality of the components first and then styled the quiz with styled components.

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://cute-pasca-d39626.netlify.app/
286 changes: 248 additions & 38 deletions package-lock.json

Large diffs are not rendered by default.

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",
"styled-components.macro": "^1.0.0"
},
"scripts": {
"start": "react-scripts start",
Expand Down
2 changes: 1 addition & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React from 'react';
import { Provider } from 'react-redux';
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { quiz } from 'reducers/quiz';

import { CurrentQuestion } from 'components/CurrentQuestion';

/* combineReducers lets us add many reducers to the state */
const reducer = combineReducers({
quiz: quiz.reducer
});
Expand Down
45 changes: 45 additions & 0 deletions src/components/Accordion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { StyledAccordion, StyledButton, AccordionH5, AnswersContainer } from './Style/GlobalStyle';

export const Accordion = () => {
const questions = useSelector((store) => store.quiz.questions);
const answers = useSelector((store) => store.quiz.answers);
const [activeAccordion, setActiveAccordion] = useState(null);

const handleAccordionClick = (index) => {
/* Checks the index of the clicked button against activeAccordion
and alter it's class */
setActiveAccordion(index === activeAccordion ? null : index);
};

return (
<StyledAccordion>
{questions.map((question, index) => (
<div key={question.id}>
<StyledButton
accordian
type="button"
className={activeAccordion === index ? 'accordionActive' : 'accordionInactive'}
onClick={() => handleAccordionClick(index)}>
<AccordionH5 style={{ color: answers[index].answer === question.options[question.correctAnswerIndex] ? 'forestgreen' : 'orangered' }}>{question.questionText}
</AccordionH5>
</StyledButton>
{activeAccordion === index && (
<AnswersContainer>
<p style={{ color: answers[index].answer === question.options[question.correctAnswerIndex] ? 'forestgreen' : 'orangered', margin: 0 }}>
<span style={{ fontWeight: 'bold' }}>Your answer:
</span> {answers[index].answer}
</p>
<p style={{ color: 'forestgreen', marginBottom: 0 }}>
<span
style={{ fontWeight: 'bold' }}>Correct answer:
</span> {question.options[question.correctAnswerIndex]}
</p>
</AnswersContainer>
)}
</div>
))}
</StyledAccordion>
);
};
89 changes: 89 additions & 0 deletions src/components/AnswerOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { quiz } from 'reducers/quiz'
import { StyledButton } from './Style/GlobalStyle';

export const AnswerOptions = () => {
const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex])
const dispatch = useDispatch()
const [answerColor, setAnswerColor] = useState('pink');
const [showSelectedColor, setShowSelectedColor] = useState(false);
const [selectedIndex, setSelectedIndex] = useState();
const [disabled, setDisabled] = useState(false);
const [showCorrectColor, setShowCorrectColor] = useState();
const [correctIndex, setCorrectIndex] = useState();
const [correctColor, setCorrectColor] = useState();

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

const handleOptionClick = (option, index) => {
dispatch(quiz.actions.submitAnswer({ questionId: question.id, answerIndex: index }))
setSelectedIndex(index);
setCorrectIndex(question.correctAnswerIndex);
setDisabled(true)

setShowSelectedColor(true);
if (index === question.correctAnswerIndex) {
setAnswerColor('olivedrab');
setShowCorrectColor(false);
setCorrectColor('olivedrab');
} else {
setAnswerColor('orangered');
setShowCorrectColor(true);
setCorrectColor('olivedrab');
}

setTimeout(() => {
setSelectedIndex(100)
dispatch(quiz.actions.goToNextQuestion())
setShowSelectedColor(false);
setAnswerColor('#D1E64B')
setShowCorrectColor(false);
setCorrectColor('');
setDisabled(false)
}, 1000)
}

const handleMouseEnter = (e) => {
e.target.style.boxShadow = '0px 0px 0px 0.5em #F36B2B';
}

const handleMouseLeave = (e) => {
e.target.style.boxShadow = '0px 0px 0px 0.5em #D1E64B';
}

const getOptionColor = (index) => {
if (showSelectedColor && index === selectedIndex) {
return answerColor;
} else if (showCorrectColor && index === correctIndex) {
return correctColor;
} else {
return '#D1E64B';
}
};

return (
<>
{question.options.map((option, index) => (
<div key={option.id} lang="en">
<StyledButton
optionbutton
lang="en"
disabled={disabled}
style={{
boxShadow: `0px 0px 0px 0.5em ${getOptionColor(index)}`
}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
type="button"
// eslint-disable-next-line max-len
onClick={() => handleOptionClick(option, index)}>
{option}
</StyledButton>
</div>
))}
</>
)
}
27 changes: 23 additions & 4 deletions src/components/CurrentQuestion.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { Starter } from 'components/Starter';
import Summary from './Summary'
import Progressbar from './Progressbar'
import RestartBtn from './RestartBtn'
import { AnswerOptions } from './AnswerOptions';
import { OptionContainer, BackgroundStarter, InnerContainer } from './Style/GlobalStyle';

export const CurrentQuestion = () => {
const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex])
const isQuizOver = useSelector((store) => store.quiz.quizOver)
const quizStarted = useSelector((store) => store.quiz.quizStarted)

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

return (
<div>
<h1>Question: {question.questionText}</h1>
</div>
<BackgroundStarter>
{!quizStarted && <Starter />}
{!isQuizOver && quizStarted && (
<InnerContainer key={question.id}>
<h1>{question.questionText}</h1>
<OptionContainer>
<AnswerOptions />
</OptionContainer>
<Progressbar />
<RestartBtn />
</InnerContainer>)}
{isQuizOver && (
<Summary />)}
</BackgroundStarter>
)
}
}
70 changes: 70 additions & 0 deletions src/components/Progressbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';

const ProgressContainer = styled.div`
width: 30vw;
height: 10px;
border-radius: 50px;
position: relative;
overflow: hidden;
display: flex;
margin-top: 5px;
justify-content: space-between;
align-items: center;
border: 0.5px solid black;

}
`;

const ProgressStatus = styled.div`
width: ${(props) => props.width};
background: linear-gradient(
90deg,
var(--yellow) 0%,
var(--orange) 100%
);
transition: width 1s ease-in-out;
height: 100%;
position: absolute;
top: 0;
`;
const Amount = styled.label`
color: black;
text-align: center;
font-size: 1.25vw;
font-weight: bold;
line-height: 2vw;
white-space: wrap;
letter-spacing: 0.1vw;

`

const Wrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 3em;
padding-bottom: 2em;
`

const ProgressBar = () => {
const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex])
const amountOfQuestions = useSelector((state) => state.quiz.questions.length);
const percentCompleted = `${useSelector(
(store) => (store.quiz.currentQuestionIndex * 100) / store.quiz.questions.length
)}%`;
return (
<Wrapper>
<Amount> THIS IS QUESTION NO &nbsp;
{question.id} / {amountOfQuestions}
</Amount>
<ProgressContainer>

<ProgressStatus width={percentCompleted} />
</ProgressContainer>
</Wrapper>
)
}
export default ProgressBar;
23 changes: 23 additions & 0 deletions src/components/RestartBtn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'
import { useDispatch } from 'react-redux'
import { quiz } from 'reducers/quiz'
import { StyledButton } from './Style/GlobalStyle'

const RestartBtn = () => {
const dispatch = useDispatch()
const onRestart = () => {
dispatch(quiz.actions.restart())
}

return (
<div>
<StyledButton
type="button"
onClick={onRestart}>
RESTART
</StyledButton>
</div>
)
}

export default RestartBtn;
34 changes: 34 additions & 0 deletions src/components/Starter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable max-len */
import React from 'react'
import { useDispatch } from 'react-redux'
import { startQuiz } from 'reducers/quiz'
import { StartContainer, StyledButton } from './Style/GlobalStyle'

export const Starter = () => {
const dispatch = useDispatch()

const handleStartQuiz = () => {
dispatch(startQuiz())
}

return (
<StartContainer>
<h1>
Welcome to the Zombie Quiz Game!
</h1>
<p>
In this game, you will be tested on your knowledge of everything zombie-related. <br />
Do you think you have what it takes to make it through the apocalypse? <br />
Put your knowledge to the test and find out! <br />
Are you ready to survive the zombie apocalypse? <br />

Let&apos;s find out!
</p>
<StyledButton
type="button"
onClick={handleStartQuiz}>
START ZOMBIE QUIZ
</StyledButton>
</StartContainer>
)
}
Loading