diff --git a/.eslintrc.json b/.eslintrc.json
index c9c0675c..90119e00 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -26,6 +26,7 @@
"react-hooks"
],
"rules": {
+ "linebreak-style": "off",
"react/function-component-definition": [
2,
{
diff --git a/README.md b/README.md
index d1f22b80..7a2567a9 100644
--- a/README.md
+++ b/README.md
@@ -10,4 +10,26 @@ Describe how you approached to problem, and what tools and techniques you used t
## 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://rococo-pony-134b75.netlify.app/
+
+# Running Locally
+
+This is a short guide on how to run a Redux quiz project locally.
+
+## Prerequisites
+
+- [Node.js](https://nodejs.org/en/) installed on your machine
+- A code editor like [VSCode](https://code.visualstudio.com/) or [Sublime Text](https://www.sublimetext.com/)
+
+## Installation
+
+1. Clone the repository or download the project files to your local machine.
+2. Open a terminal and navigate to the project directory.
+3. Run `npm install` to install all the project dependencies including `react-redux`, `@reduxjs/toolkit`, and `react-router-dom`.
+
+## Running the Project
+
+1. After the installation is complete, run `npm start` to start the development server.
+2. Once the server is started, you can view the project in your web browser by navigating to [http://localhost:3000](http://localhost:3000).
+
+That's it! You now have the Redux project up and running on your local machine with all the required dependencies installed.
diff --git a/package-lock.json b/package-lock.json
index 8bd1eecb..59e14584 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@babel/eslint-parser": "^7.18.9",
- "@reduxjs/toolkit": "^1.8.3",
+ "@reduxjs/toolkit": "^1.9.3",
"eslint": "^8.21.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
@@ -18,7 +18,8 @@
"eslint-plugin-react-hooks": "^4.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "^8.0.2",
+ "react-redux": "^8.0.5",
+ "react-router-dom": "^6.10.0",
"react-scripts": "^5.0.1"
},
"devDependencies": {
@@ -3128,14 +3129,14 @@
}
},
"node_modules/@reduxjs/toolkit": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.3.tgz",
- "integrity": "sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==",
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz",
+ "integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==",
"dependencies": {
- "immer": "^9.0.7",
- "redux": "^4.1.2",
- "redux-thunk": "^2.4.1",
- "reselect": "^4.1.5"
+ "immer": "^9.0.16",
+ "redux": "^4.2.0",
+ "redux-thunk": "^2.4.2",
+ "reselect": "^4.1.7"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
@@ -3150,6 +3151,14 @@
}
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz",
+ "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -8873,9 +8882,9 @@
}
},
"node_modules/immer": {
- "version": "9.0.15",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz",
- "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==",
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -14448,9 +14457,9 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-redux": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
- "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==",
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
+ "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"dependencies": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
@@ -14499,6 +14508,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz",
+ "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==",
+ "dependencies": {
+ "@remix-run/router": "1.5.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz",
+ "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==",
+ "dependencies": {
+ "@remix-run/router": "1.5.0",
+ "react-router": "6.10.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -14655,9 +14694,9 @@
}
},
"node_modules/redux-thunk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
- "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
+ "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"peerDependencies": {
"redux": "^4"
}
@@ -14818,9 +14857,9 @@
"dev": true
},
"node_modules/reselect": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
- "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz",
+ "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A=="
},
"node_modules/resolve": {
"version": "1.22.1",
@@ -19654,16 +19693,21 @@
}
},
"@reduxjs/toolkit": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.3.tgz",
- "integrity": "sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==",
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz",
+ "integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==",
"requires": {
- "immer": "^9.0.7",
- "redux": "^4.1.2",
- "redux-thunk": "^2.4.1",
- "reselect": "^4.1.5"
+ "immer": "^9.0.16",
+ "redux": "^4.2.0",
+ "redux-thunk": "^2.4.2",
+ "reselect": "^4.1.7"
}
},
+ "@remix-run/router": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz",
+ "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg=="
+ },
"@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -23941,9 +23985,9 @@
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="
},
"immer": {
- "version": "9.0.15",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz",
- "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ=="
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
},
"import-fresh": {
"version": "3.3.0",
@@ -27880,9 +27924,9 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-redux": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
- "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==",
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
+ "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"requires": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
@@ -27905,6 +27949,23 @@
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
"dev": true
},
+ "react-router": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz",
+ "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==",
+ "requires": {
+ "@remix-run/router": "1.5.0"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz",
+ "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==",
+ "requires": {
+ "@remix-run/router": "1.5.0",
+ "react-router": "6.10.0"
+ }
+ },
"react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -28030,9 +28091,9 @@
}
},
"redux-thunk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
- "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
+ "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"requires": {}
},
"regenerate": {
@@ -28160,9 +28221,9 @@
"dev": true
},
"reselect": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
- "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz",
+ "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A=="
},
"resolve": {
"version": "1.22.1",
diff --git a/package.json b/package.json
index 58da74eb..9b4d7349 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"private": true,
"dependencies": {
"@babel/eslint-parser": "^7.18.9",
- "@reduxjs/toolkit": "^1.8.3",
+ "@reduxjs/toolkit": "^1.9.3",
"eslint": "^8.21.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
@@ -13,7 +13,8 @@
"eslint-plugin-react-hooks": "^4.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "^8.0.2",
+ "react-redux": "^8.0.5",
+ "react-router-dom": "^6.10.0",
"react-scripts": "^5.0.1"
},
"scripts": {
diff --git a/public/assets/linkedinlogo.png b/public/assets/linkedinlogo.png
new file mode 100644
index 00000000..50aeee7c
Binary files /dev/null and b/public/assets/linkedinlogo.png differ
diff --git a/src/App.js b/src/App.js
index 690bd373..bcd97f4a 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,9 +1,11 @@
import React from 'react';
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { quiz } from 'reducers/quiz';
+import { WelcomePage } from 'pages/WelcomePage';
-import { CurrentQuestion } from 'components/CurrentQuestion';
+import { Question } from 'pages/Question';
const reducer = combineReducers({
quiz: quiz.reducer
@@ -14,7 +16,12 @@ const store = configureStore({ reducer });
export const App = () => {
return (
-
+
+
+ } />
+ } />
+
+
);
}
diff --git a/src/components/AnswerButton.js b/src/components/AnswerButton.js
new file mode 100644
index 00000000..ce36f233
--- /dev/null
+++ b/src/components/AnswerButton.js
@@ -0,0 +1,47 @@
+/* eslint-disable max-len */
+/* eslint-disable no-nested-ternary */
+/* eslint-disable no-shadow */
+import React, { useState } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import { quiz } from '../reducers/quiz';
+
+export const AnswerButton = ({ index, option, setGoToNextButton }) => {
+ const dispatch = useDispatch();
+ const [activeBtn, setActiveBtn] = useState(false);
+
+ // Gets all question in the store
+ const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex]);
+
+ // Gets the answers the user has given
+ const usersAnswer = useSelector((state) => state.quiz.answers[state.quiz.currentQuestionIndex]);
+
+ const onAnswerSubmit = (questionId, answerIndex) => {
+ dispatch(quiz.actions.submitAnswer({
+ questionId, answerIndex
+ }));
+ if (question.correctAnswerIndex === answerIndex) {
+ window.alert('Yay YOU!')
+ dispatch(quiz.actions.goToNextQuestion())
+ } else {
+ window.alert('Wrong answer!')
+ }
+ setActiveBtn(true);
+ setGoToNextButton(true);
+ }
+
+ const correctAnswer = usersAnswer && index === question.correctAnswerIndex;
+
+ return (
+ /* If activeBtn is true, it checks whether the answer is correct or incorrect based on the correctAnswer variable.
+ If correctAnswer is true, it sets the class to 'correct', which applies the appropriate styles to indicate a correct answer.
+ If correctAnswer is false, it sets the class to 'wrong', which applies the appropriate styles to indicate an incorrect answer.
+ If activeBtn is false, it sets the class to 'default', which applies the default styles to the button. */
+
+ );
+};
diff --git a/src/components/CurrentQuestion.css b/src/components/CurrentQuestion.css
new file mode 100644
index 00000000..1d91b898
--- /dev/null
+++ b/src/components/CurrentQuestion.css
@@ -0,0 +1,32 @@
+.eslint-hater h1 {
+ font-family: futura-pt, 'sans-serif';
+ font-weight: 900;
+ letter-spacing: -0.01em;
+ color: rgb(0, 24, 164);
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.answerButton {
+ place-self: center;
+}
+
+.answer-buttons {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.eslint-hater-bottom {
+ display: flex;
+ flex-direction: column;
+ gap: 11px;
+}
+
+.eslint-hater {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
diff --git a/src/components/CurrentQuestion.js b/src/components/CurrentQuestion.js
index 36ee2224..2055b0c9 100644
--- a/src/components/CurrentQuestion.js
+++ b/src/components/CurrentQuestion.js
@@ -1,16 +1,51 @@
-import React from 'react'
-import { useSelector } from 'react-redux'
+import React, { useState } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import { NextButton } from './NextButton';
+import { ProgressBar } from './ProgressBar';
+import { AnswerButton } from './AnswerButton';
+import { SummaryPage } from '../pages/Summary';
+import { quiz } from '../reducers/quiz';
+import './CurrentQuestion.css';
export const CurrentQuestion = () => {
- const question = useSelector((state) => state.quiz.questions[state.quiz.currentQuestionIndex])
+ console.log(quiz);
+ const dispatch = useDispatch();
+ const [goToNextButton, setGoToNextButton] = useState(false);
+ const question = useSelector(
+ (state) => state.quiz.questions[state.quiz.currentQuestionIndex]
+ );
+ const quizOver = useSelector((state) => state.quiz.quizOver);
if (!question) {
- return
Oh no! I could not find the current question!
+ return Oh no! I could not find the current question!
;
}
+ const moveToNext = () => {
+ dispatch(quiz.actions.goToNextQuestion());
+ setGoToNextButton(false);
+ };
return (
-
-
Question: {question.questionText}
-
- )
-}
+ <>
+ {quizOver && }
+ {!quizOver && (
+
+
Question: {question.questionText}
+
+ {question.options.map((option, index) => (
+
+ ))}
+
+
+ {goToNextButton &&
}
+
+
+
+ )}
+ >
+ );
+};
\ No newline at end of file
diff --git a/src/components/Footer.css b/src/components/Footer.css
new file mode 100644
index 00000000..40abe06d
--- /dev/null
+++ b/src/components/Footer.css
@@ -0,0 +1,51 @@
+/* Footer */
+
+footer {
+ /* margin-top: 20px;
+ height: 120px;
+ padding: 5px; */
+ display: flex;
+ flex-direction: column;
+ align-self: flex-end;
+ border-color: rgb(0, 37, 255);
+ border-width: 0px 16px 16px 16px;
+ border-style: solid;
+ }
+
+ footer p {
+ text-align: center;
+ color: black;
+ text-shadow: 1px 1px 2px white;
+ }
+
+ .contact-me {
+ display:flex;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+ padding: 5px;
+ /*
+ margin-top: 10px; */
+ }
+
+ .contact-logo{
+ width: 20px;
+ height: 20px;
+ object-fit: cover;
+ opacity: .75;
+ }
+
+ .contact-logo:hover{
+ transform: scale(1.2);
+ opacity: 1;
+ }
+
+ .about-me {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 15px;
+ color: rgb(0, 24, 164);
+ text-shadow: 1px 1px 2px white;
+ text-align: center;
+ }
\ No newline at end of file
diff --git a/src/components/NextButton.css b/src/components/NextButton.css
new file mode 100644
index 00000000..40a86b90
--- /dev/null
+++ b/src/components/NextButton.css
@@ -0,0 +1,27 @@
+.nextButton {
+ cursor: pointer;
+ text-underline-offset: 6px;
+ font-family: futura-pt, "sans-serif";
+ font-weight: 600;
+ transition: all 0.2s ease 0s;
+ /* display: inline-block; */
+ padding: 18px 30px;
+ border-radius: 50px;
+ text-decoration: none;
+ font-size: 18px;
+ border: 2px solid rgb(10, 37, 255);
+ /* place-self: flex-start; */
+ color: rgb(10, 37, 255);
+ background: transparent;
+ align-self: center;
+ margin-top:40px;
+ margin-bottom:40px;
+ /* this is the important stuff 👇 */
+animation: bounce 1s infinite alternate;
+}
+
+@keyframes bounce {
+ 0% {transform: translateY(0);}
+ 100% {transform: translateY(50px);}
+ }
+
diff --git a/src/components/NextButton.js b/src/components/NextButton.js
new file mode 100644
index 00000000..faf7886b
--- /dev/null
+++ b/src/components/NextButton.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import { quiz } from '../reducers/quiz';
+import './NextButton.css';
+
+export const NextButton = () => {
+ const dispatch = useDispatch();
+ const goToNextQuestion = () => {
+ dispatch(quiz.actions.goToNextQuestion());
+ };
+ return (
+
+ );
+};
diff --git a/src/components/ProgressBar.css b/src/components/ProgressBar.css
new file mode 100644
index 00000000..09fa4692
--- /dev/null
+++ b/src/components/ProgressBar.css
@@ -0,0 +1,25 @@
+.progress-bar {
+ font-family: futura-pt, 'sans-serif';
+ letter-spacing: -0.01em;
+ font-size: 32px;
+ line-height: min(max(50px, 6vw), 53px);
+ color: rgb(0, 24, 164);
+}
+.progress {
+ width: 100%;
+ height: 10px;
+ appearance: none;
+ background-color: #f4f4f4;
+ border-radius: 20px;
+ overflow: hidden;
+}
+
+.progress::-webkit-progress-bar {
+ background-color: #f4f4f4;
+ border-radius: 20px;
+}
+
+.progress::-webkit-progress-value {
+ background-color: rgba(0, 37, 255, 1);
+ border-radius: 20px;
+}
diff --git a/src/components/ProgressBar.js b/src/components/ProgressBar.js
new file mode 100644
index 00000000..12eec768
--- /dev/null
+++ b/src/components/ProgressBar.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import './ProgressBar.css';
+
+export const ProgressBar = () => {
+ const questions = useSelector((state) => state.quiz.questions);
+ const currentQuestionIndex = useSelector((state) => {
+ return state.quiz.questions[state.quiz.currentQuestionIndex];
+ });
+
+ const progress = (currentQuestionIndex.id / questions.length) * 100;
+
+ return (
+
+
+ Question: {currentQuestionIndex.id} / {questions.length}
+
+
+
+ );
+};
diff --git a/src/components/ResetButton.css b/src/components/ResetButton.css
new file mode 100644
index 00000000..2374b707
--- /dev/null
+++ b/src/components/ResetButton.css
@@ -0,0 +1,35 @@
+.resetButton {
+ cursor: pointer;
+ text-underline-offset: 6px;
+ /*font-family: futura-pt, 'sans-serif'; */
+ font-weight: 600;
+ transition: all 0.2s ease 0s;
+ display: inline-block;
+ padding: 18px 30px;
+ border-radius: 50px;
+ text-decoration: none;
+ font-size: 18px;
+ border: 2px solid rgb(10, 37, 255);
+ place-self: flex-start;
+ color: rgb(255, 255, 255);
+ background: rgb(10, 37, 255);
+
+ /* this is the important stuff 👇 */
+ animation: bounce 1s infinite alternate;
+}
+
+@keyframes bounce {
+ 0% {
+ transform: translateY(0);
+ }
+ 100% {
+ transform: translateY(50px);
+ }
+}
+.resetButtonContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 40px;
+ margin-bottom: 40px;
+}
diff --git a/src/components/ResetButton.js b/src/components/ResetButton.js
new file mode 100644
index 00000000..3849fdbb
--- /dev/null
+++ b/src/components/ResetButton.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import { quiz } from '../reducers/quiz';
+import './ResetButton.css';
+
+export const ResetButton = () => {
+ const dispatch = useDispatch();
+ const restart = () => {
+ dispatch(quiz.actions.restart());
+ };
+ return (
+
+
+
+ );
+};
diff --git a/src/components/answerbutton.css b/src/components/answerbutton.css
new file mode 100644
index 00000000..6257da33
--- /dev/null
+++ b/src/components/answerbutton.css
@@ -0,0 +1,30 @@
+/* button {
+ background-color: #3498db;
+ color: #fff;
+ font-size: 16px;
+ padding: 12px 24px;
+ border-radius: 12px;
+ border: none;
+ cursor: pointer;
+}
+
+button.default:hover {
+ background-color: #2980b9;
+}
+
+button.default:active {
+ background-color: #21618c;
+}
+
+button.correct {
+ background-color: #2ecc71;
+}
+
+button.wrong {
+ background-color: #e74c3c;
+}
+
+button.default {
+ background-color: #bdc3c7;
+}
+*/
\ No newline at end of file
diff --git a/src/components/footer.js b/src/components/footer.js
new file mode 100644
index 00000000..5896e69a
--- /dev/null
+++ b/src/components/footer.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import './Footer.css';
+
+export const Footer = () => {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/img/tech.png b/src/img/tech.png
new file mode 100644
index 00000000..0d2911e9
Binary files /dev/null and b/src/img/tech.png differ
diff --git a/src/img/technigo.jpg b/src/img/technigo.jpg
new file mode 100644
index 00000000..2e16f73c
Binary files /dev/null and b/src/img/technigo.jpg differ
diff --git a/src/pages/Question.js b/src/pages/Question.js
new file mode 100644
index 00000000..7aaf870c
--- /dev/null
+++ b/src/pages/Question.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { CurrentQuestion } from 'components/CurrentQuestion';
+import './question.css';
+
+export const Question = () => {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/Summary.js b/src/pages/Summary.js
new file mode 100644
index 00000000..804853aa
--- /dev/null
+++ b/src/pages/Summary.js
@@ -0,0 +1,39 @@
+/* eslint-disable implicit-arrow-linebreak */
+/* eslint-disable react/jsx-no-useless-fragment */
+import React from 'react';
+import { ResetButton } from 'components/ResetButton';
+import { useSelector } from 'react-redux';
+import './summary.css';
+
+export const SummaryPage = () => {
+ // Variable to calculate how many correct answer the user has given
+ const correctAnswer = useSelector(
+ (state) =>
+ state.quiz.answers.filter((answer) => answer.isCorrect === true).length
+ );
+ const answers = useSelector((state) => state.quiz.answers);
+
+ const userSummary = () => {
+ if (correctAnswer === 5) {
+ return 'Youa are a genius 🧠';
+ } else if (correctAnswer === 4) {
+ return 'Well done 👌';
+ } else if (correctAnswer === 3) {
+ return 'Well done 👌';
+ } else {
+ return 'You need to study more 🤦';
+ }
+ };
+
+ // console.log(correctAnswer);
+ return (
+
+
Oh wow, what a ride!
+
+ Your final score was: {correctAnswer} out of {answers.length}
+
+
+
{userSummary()}
+
+ );
+};
diff --git a/src/pages/WelcomePage.js b/src/pages/WelcomePage.js
new file mode 100644
index 00000000..47624151
--- /dev/null
+++ b/src/pages/WelcomePage.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import './welcomepage.css';
+import { useNavigate } from 'react-router-dom';
+import { Footer } from 'components/footer';
+
+export const WelcomePage = () => {
+ const navigate = useNavigate();
+ const goToQuiz = () => {
+ navigate('/quiz');
+ };
+ return (
+
+
+
+
Hello there friend 👋
+
Ready to Rumble with React?
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/question.css b/src/pages/question.css
new file mode 100644
index 00000000..3f97b3cd
--- /dev/null
+++ b/src/pages/question.css
@@ -0,0 +1,6 @@
+.questions-background {
+ height: 100vh;
+ border-color: rgb(0, 37, 255);
+ border-width: 16px 16px 0px 16px;
+ border-style: solid;
+}
\ No newline at end of file
diff --git a/src/pages/summary.css b/src/pages/summary.css
new file mode 100644
index 00000000..ee85ec66
--- /dev/null
+++ b/src/pages/summary.css
@@ -0,0 +1,22 @@
+.summary {
+ height: 100vh;
+ border-color: rgb(0, 37, 255);
+ border-width: 16px 16px 16px 16px;
+ border-style: solid;
+ display: flex;
+ flex-direction: column;
+}
+
+.summary h1, h3 {
+ font-family: futura-pt, "sans-serif";
+ font-weight: 900;
+ letter-spacing: -0.01em;
+ color: rgb(0, 24, 164);
+ text-align: center;
+ padding-top: 40px;
+}
+
+.summary p {
+ padding-top: 40px;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/src/pages/welcomepage.css b/src/pages/welcomepage.css
new file mode 100644
index 00000000..d2ed2ad1
--- /dev/null
+++ b/src/pages/welcomepage.css
@@ -0,0 +1,53 @@
+.welcome-background {
+ height: 400px;
+ background-image: url(../img/tech.png);
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ /* background-position: center -550px; */
+}
+
+.content {
+ text-align: center;
+ padding-top: 16px;
+ border-color: rgb(0, 37, 255);
+ border-width: 16px 16px 0px 16px;
+ border-style: solid;
+}
+.welcome-text-container h1 {
+ font-family: futura-pt, 'sans-serif';
+ font-weight: 900;
+ letter-spacing: -0.01em;
+ color: rgb(0, 24, 164);
+}
+
+button {
+ cursor: pointer;
+ text-underline-offset: 6px;
+ /*font-family: futura-pt, 'sans-serif'; */
+ font-weight: 600;
+ transition: all 0.2s ease 0s;
+ display: inline-block;
+ padding: 18px 30px;
+ border-radius: 50px;
+ text-decoration: none;
+ font-size: 18px;
+ border: 2px solid rgb(10, 37, 255);
+ /* place-self: flex-start; */
+ color: rgb(255, 255, 255);
+ background: rgb(10, 37, 255);
+ align-self: center;
+}
+.welcomeButton {
+ /* this is the important stuff 👇 */
+ animation: bounce 1s infinite alternate;
+}
+
+@keyframes bounce {
+ 0% {
+ transform: translateY(0);
+ }
+ 100% {
+ transform: translateY(50px);
+ }
+}
diff --git a/src/reducers/quiz.js b/src/reducers/quiz.js
index a38bbf68..cf8d92d4 100644
--- a/src/reducers/quiz.js
+++ b/src/reducers/quiz.js
@@ -1,23 +1,76 @@
-import { createSlice } from '@reduxjs/toolkit'
+/* eslint-disable comma-dangle */
+import { createSlice } from '@reduxjs/toolkit';
// Change these to your own questions!
const questions = [
- { id: 1, questionText: 'Who set the Olympic record for the 100m dash in 2012?', options: ['Usain Bolt', 'Justin Gatlin', 'Tyson Gay', 'Asafa Powell'], correctAnswerIndex: 0 },
- { id: 2, questionText: 'When was Michael Phelps last named male World Swimmer of the Year?', options: ['2012', '2014', '2016', '2018'], correctAnswerIndex: 2 }
-]
+ {
+ id: 1,
+ questionText: 'What is React?',
+ options: [
+ 'A front-end JavaScript library',
+ 'A back-end programming language',
+ 'A mobile application development platform',
+ 'A database management system',
+ ],
+ correctAnswerIndex: 0,
+ },
+ {
+ id: 2,
+ questionText: 'What is JSX?',
+ options: [
+ 'A type of HTML used in React components',
+ 'A JavaScript extension for writing React code',
+ 'A CSS framework for styling React components',
+ 'A backend server for React applications',
+ ],
+ correctAnswerIndex: 1,
+ },
+ {
+ id: 3,
+ questionText: 'What is the Virtual DOM?',
+ options: [
+ 'A real-time representation of the HTML DOM used for performance optimization',
+ 'A way to manage multiple React components on a page',
+ 'A tool for debugging React applications',
+ 'A database used to store React component data',
+ ],
+ correctAnswerIndex: 0,
+ },
+ {
+ id: 4,
+ questionText: 'What is a React component?',
+ options: [
+ 'A small, reusable piece of code that can be rendered on a web page',
+ 'A programming language used to create React applications',
+ 'A database used to store React component data',
+ 'A tool for debugging React applications',
+ ],
+ correctAnswerIndex: 0,
+ },
+ {
+ id: 5,
+ questionText: 'What is the purpose of state in React?',
+ options: [
+ 'To store and manage data that can change over time',
+ 'To render components on the page',
+ 'To handle user events like clicks and keyboard inputs',
+ 'To control the overall layout of the web page',
+ ],
+ correctAnswerIndex: 0,
+ },
+];
const initialState = {
questions,
answers: [],
currentQuestionIndex: 0,
- quizOver: false
-}
+ quizOver: false,
+};
export const quiz = createSlice({
name: 'quiz',
initialState,
reducers: {
-
/**
* Use this action when a user selects an answer to the question.
* The answer will be stored in the `quiz.answers` state with the
@@ -34,15 +87,19 @@ export const quiz = createSlice({
* and `answerIndex` keys. See the readme for more details.
*/
submitAnswer: (state, action) => {
- const { questionId, answerIndex } = action.payload
- const question = state.questions.find((q) => q.id === questionId)
+ const { questionId, answerIndex } = action.payload;
+ const question = state.questions.find((q) => q.id === questionId);
if (!question) {
- throw new Error('Could not find question! Check to make sure you are passing the question id correctly.')
+ throw new Error(
+ 'Could not find question! Check to make sure you are passing the question id correctly.'
+ );
}
if (question.options[answerIndex] === undefined) {
- throw new Error(`You passed answerIndex ${answerIndex}, but it is not in the possible answers array!`)
+ throw new Error(
+ `You passed answerIndex ${answerIndex}, but it is not in the possible answers array!`
+ );
}
state.answers.push({
@@ -50,8 +107,8 @@ export const quiz = createSlice({
answerIndex,
question,
answer: question.options[answerIndex],
- isCorrect: question.correctAnswerIndex === answerIndex
- })
+ isCorrect: question.correctAnswerIndex === answerIndex,
+ });
},
/**
@@ -63,9 +120,9 @@ export const quiz = createSlice({
*/
goToNextQuestion: (state) => {
if (state.currentQuestionIndex + 1 === state.questions.length) {
- state.quizOver = true
+ state.quizOver = true;
} else {
- state.currentQuestionIndex += 1
+ state.currentQuestionIndex += 1;
}
},
@@ -77,8 +134,7 @@ export const quiz = createSlice({
* This action does not require a payload.
*/
restart: () => {
- return initialState
- }
-
- }
-})
+ return initialState;
+ },
+ },
+});