diff --git a/frontend/src/assets/stylesheets/components/App.scss b/frontend/src/assets/stylesheets/components/App.scss
new file mode 100644
index 0000000..f14519b
--- /dev/null
+++ b/frontend/src/assets/stylesheets/components/App.scss
@@ -0,0 +1,23 @@
+.app {
+ text-align: center;
+
+ &__name {
+ font-size: 3.125rem;
+ }
+
+ &__instructions {
+ font-size: 1.25rem;
+ margin-top: 3rem;
+ margin-bottom: 3rem;
+ }
+
+ &__list {
+ margin-bottom: 2rem;
+ margin-left: 2.5rem;
+ list-style-type: disc;
+
+ > li {
+ margin-bottom: 0.5rem;
+ }
+ }
+}
diff --git a/frontend/src/assets/stylesheets/components/CodeEditor.scss b/frontend/src/assets/stylesheets/components/CodeEditor.scss
index 04a9b76..9164f7d 100755
--- a/frontend/src/assets/stylesheets/components/CodeEditor.scss
+++ b/frontend/src/assets/stylesheets/components/CodeEditor.scss
@@ -3,7 +3,7 @@
left: 0;
right: 0;
bottom: 0;
- top: 0;
+ top: 300px;
display: flex;
align-items: center;
@@ -153,11 +153,12 @@
.code_editor__background {
position: absolute;
- top: 0;
+ top: -150px;
left: 0;
right: 0;
bottom: 0;
- background-color: rgba(0, 0, 0, 0.3);
+ height:100vh;
+ //background-color: rgba(0, 0, 0, 0.3);
z-index: 0;
cursor: pointer;
}
diff --git a/frontend/src/assets/stylesheets/components/Game.scss b/frontend/src/assets/stylesheets/components/Game.scss
index 3052783..05aac50 100755
--- a/frontend/src/assets/stylesheets/components/Game.scss
+++ b/frontend/src/assets/stylesheets/components/Game.scss
@@ -12,7 +12,6 @@
}
&__congratulation {
- margin-bottom: 5rem;
font-size: 1.5rem;
}
}
diff --git a/frontend/src/assets/stylesheets/components/Playthrough.scss b/frontend/src/assets/stylesheets/components/Playthrough.scss
index 454309c..cb6e95b 100755
--- a/frontend/src/assets/stylesheets/components/Playthrough.scss
+++ b/frontend/src/assets/stylesheets/components/Playthrough.scss
@@ -1,8 +1,8 @@
.playthrough {
padding: 2rem 0;
- max-width: 700px;
margin: 0 auto;
text-align: left;
+ padding-top: 0;
&__title {
font-size: 2.2rem;
@@ -19,7 +19,6 @@
}
&__list {
- margin-bottom: 2rem;
margin-left: 2.5rem;
list-style-type: disc;
diff --git a/frontend/src/assets/stylesheets/components/Results.scss b/frontend/src/assets/stylesheets/components/Results.scss
index 7f4d558..a98c9b4 100755
--- a/frontend/src/assets/stylesheets/components/Results.scss
+++ b/frontend/src/assets/stylesheets/components/Results.scss
@@ -1,5 +1,6 @@
.results {
padding: 2rem 0;
+ padding-top: 0;
&__title {
font-size: 2.2rem;
@@ -13,14 +14,50 @@
}
.result {
- margin: 0 auto;
+ //margin: 0 auto;
display: grid;
- grid-template-columns: 2fr 1fr;
- margin-bottom: 0.75rem;
- max-width: 300px;
+ //grid-template-columns: 2fr 1fr;
+ //margin-bottom: 0.75rem;
+ //max-width: 300px;
font-size: 1.25rem;
+ text-align: center;
&__category {
font-weight: bold;
}
+ .column-width{
+ width: 33%;
+ }
+ /* Result Table */
+ #title {
+ text-align: left;
+ font-family: arial, sans-serif;
+ }
+ #quizResults {
+ text-align: left;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ border-collapse: collapse;
+ border: 1px solid #6c757d;
+ width: 100%;
+ }
+ #quizResults td, #quizResults th {
+ border: 1px solid #6c757d;
+ font-size: 16px;
+ padding: 10px;
+ }
+ #quizResults th {
+ text-align: center;
+ color: #fed136;
+ background-color: rgb(72, 72, 72);
+ }
+ .answer-wrong{
+ color: #721c24;
+ background-color: #f8d7da;
+ }
+ .answer-correct{
+ color: #155724;
+ background-color: #d4edda;
+ }
}
diff --git a/frontend/src/assets/stylesheets/main.scss b/frontend/src/assets/stylesheets/main.scss
index b2b740e..e42688b 100755
--- a/frontend/src/assets/stylesheets/main.scss
+++ b/frontend/src/assets/stylesheets/main.scss
@@ -7,6 +7,8 @@
@import "base/base";
@import "base/helpers";
+@import "components/App";
+
@import "components/CodeEditor";
@import "components/Header";
@import "components/Instructions";
@@ -22,3 +24,4 @@
@import "pages/Supplementary";
@import "pages/CaseStudy";
+@import "pages/quiz";
diff --git a/frontend/src/assets/stylesheets/pages/CaseStudy.scss b/frontend/src/assets/stylesheets/pages/CaseStudy.scss
index 9c40dee..ef6a806 100755
--- a/frontend/src/assets/stylesheets/pages/CaseStudy.scss
+++ b/frontend/src/assets/stylesheets/pages/CaseStudy.scss
@@ -1,5 +1,4 @@
.study {
- width: 960px;
margin: 2rem auto;
font-size: 1.5rem;
diff --git a/frontend/src/assets/stylesheets/pages/quiz.scss b/frontend/src/assets/stylesheets/pages/quiz.scss
new file mode 100644
index 0000000..9a2c2a6
--- /dev/null
+++ b/frontend/src/assets/stylesheets/pages/quiz.scss
@@ -0,0 +1,231 @@
+.quiz {
+
+ .quizHeader {
+ text-align: center;
+ margin-top: 110px;
+ }
+
+ .body {
+ margin: 0;
+ padding: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ background: #eee;
+
+ }
+
+ code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ .body {
+ margin: 0;
+ padding: 0;
+ background: #eee;
+ font-family: sans-serif;
+ }
+
+ .container {
+ position: relative;
+ max-width: 800px;
+ margin: 0 auto;
+ background: #fff;
+ border-radius: 3px;
+ }
+
+ @media (min-width: 769px) {
+ .container {
+ margin: 2.5rem auto;
+ }
+ }
+
+ /* Result Component */
+ .result {
+ padding: 1.5rem 2.5rem;
+ }
+
+ /* Quiz Component */
+ .answerOptions {
+ margin: 16px;
+ padding: 0;
+ list-style: none;
+ display: list-item;
+ }
+
+ /* QuestionCount Component */
+ .questionCount {
+ padding: 1.5rem 2.5rem 0;
+ font-size: 14px;
+ }
+
+ /* Question Component */
+ .question {
+ margin: 0;
+ padding: 0.5rem 2.5rem 1.5rem 2.5rem;
+ }
+
+ /* AnswerOption Component */
+ .answerOption {
+ border-top: 1px solid #eee;
+ }
+
+ .answerOption:hover {
+ background-color: #eefbfe;
+ }
+
+ .checkboxCustomButton {
+ position: absolute;
+ width: auto;
+ opacity: 0;
+ }
+
+ .checkboxCustomButton,
+ .checkboxCustomLabel {
+ display: inline-block;
+ vertical-align: middle;
+ cursor: pointer;
+ }
+
+ .checkboxCustomLabel {
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 1.5rem 2.5rem 1.5rem 5rem;
+ font-size: 16px;
+ line-height: 1.5;
+ text-align: left;
+ }
+
+ .checkboxCustomButton ~ .checkboxCustomLabel:before {
+ position: absolute;
+ top: 20px;
+ left: 38px;
+ width: 28px;
+ height: 28px;
+ content: '';
+ display: inline-block;
+ vertical-align: middle;
+ background: #fff;
+ border: 1px solid #bbb;
+ transition: all 0.3s;
+ }
+
+ .checkboxCustomButton:checked ~ .checkboxCustomLabel:before {
+ content: '';
+ background: #8bc53f no-repeat;
+ background-size: 27px;
+ border-color: #8bc53f;
+ }
+
+ .checkboxCustomButton:checked ~ .checkboxCustomLabel:after {
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+
+ .radioCustomButton {
+ position: absolute;
+ width: auto;
+ opacity: 0;
+ }
+
+ .radioCustomButton,
+ .radioCustomLabel {
+ display: inline-block;
+ vertical-align: middle;
+ cursor: pointer;
+ }
+
+ .radioCustomLabel {
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 1.5rem 2.5rem 1.5rem 5rem;
+ font-size: 16px;
+ line-height: 1.5;
+ text-align: left;
+ }
+
+ .radioCustomButton ~ .radioCustomLabel:before {
+ position: absolute;
+ top: 20px;
+ left: 38px;
+ width: 28px;
+ height: 28px;
+ content: '';
+ display: inline-block;
+ vertical-align: middle;
+ background: #fff;
+ border: 1px solid #bbb;
+ border-radius: 50%;
+ transition: all 0.3s;
+ }
+
+ .radioCustomButton:checked ~ .radioCustomLabel:before {
+ content: '';
+ background: #8bc53f no-repeat;
+ background-size: 27px;
+ border-color: #8bc53f;
+ }
+
+ .radioCustomButton:checked ~ .radioCustomButton:after {
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+
+ .nextButton {
+ margin: 20px;
+ padding: 8px !important;
+ }
+
+ .align-right {
+ text-align: right;
+ }
+
+ .top-margin {
+ margin-top: 150px !important;
+ }
+
+ /* Animation */
+ .fade-enter {
+ opacity: 0;
+ }
+
+ .fade-enter.fade-enter-active {
+ opacity: 1;
+ transition: opacity 0.5s ease-in-out 0.3s;
+ }
+
+ .fade-leave {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ opacity: 1;
+ }
+
+ .fade-leave.fade-leave-active {
+ opacity: 0;
+ transition: opacity 0.5s ease-in-out;
+ }
+
+ .fade-appear {
+ opacity: 0;
+ }
+
+ .fade-appear.fade-appear-active {
+ opacity: 1;
+ transition: opacity 0.5s ease-in-out;
+ }
+
+
+}
diff --git a/frontend/src/components/body/quiz.js b/frontend/src/components/body/quiz.js
index 0791b92..9330a1b 100644
--- a/frontend/src/components/body/quiz.js
+++ b/frontend/src/components/body/quiz.js
@@ -1,17 +1,17 @@
import React from "react";
+import Quiz from "./../../quiz/App"
import "./../../vendor/bootstrap/css/bootstrap.min.css";
import "./../../css/agency.min.css";
import "./../../css/style.css";
-import Form from "./../forms/form";
-const Quiz = ({ title, description, links }) => {
+const Reading = ({ title, description, links }) => {
if (links === undefined) {
links = [null, null, null];
}
return (
-
+
@@ -22,13 +22,11 @@ const Quiz = ({ title, description, links }) => {
-
-
-
+
);
};
-export default Quiz;
+export default Reading;
diff --git a/frontend/src/quiz/App.css b/frontend/src/quiz/App.css
new file mode 100644
index 0000000..659e7f9
--- /dev/null
+++ b/frontend/src/quiz/App.css
@@ -0,0 +1,24 @@
+.App-logo {
+ animation: spin infinite 20s linear;
+ height: 80px;
+}
+
+.App-header {
+ background-color: #222;
+ padding: 20px;
+ color: white;
+ text-align: center;
+}
+
+.App-intro {
+ font-size: large;
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/frontend/src/quiz/App.js b/frontend/src/quiz/App.js
new file mode 100644
index 0000000..15081e0
--- /dev/null
+++ b/frontend/src/quiz/App.js
@@ -0,0 +1,206 @@
+import React, {Component} from 'react';
+import quizQuestions from './api/quizQuestions';
+import Quiz from './components/Quiz';
+import Result from './components/Result';
+import logo from './svg/logo.svg';
+import './App.css';
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ counter: 0,
+ questionId: 1,
+ question: '',
+ answerOptions: [],
+ answer: '',
+ result: '',
+ myCount: {},
+ disableNextQuestion: true,
+ selectedAnswers: {},
+ multiChoice: false
+ };
+
+ this.handleAnswerSelected = this.handleAnswerSelected.bind(this);
+ this.setNextQuestion = this.setNextQuestion.bind(this)
+ }
+
+ componentWillMount() {
+ for (var i = 0; i < quizQuestions.length; i++) {
+ for (var x = 0; x < quizQuestions[i]['answers'].length; x++) {
+ quizQuestions[i]['answers'][x]['type'] = "" + x;
+ }
+ }
+
+ console.log(quizQuestions);
+
+ }
+
+ componentDidMount() {
+ for (var i = 0; i < quizQuestions.length; i++) {
+ quizQuestions[i]['answers'].sort(function (a, b) {
+ return parseInt(a.type) - parseInt(b.type);
+ });
+ }
+ this.setState({
+ question: quizQuestions[0].question,
+ answerOptions: quizQuestions[0]['answers'],
+ multiChoice: quizQuestions[0]['multiChoice']
+ });
+ }
+
+ shuffleArray(array) {
+ var currentIndex = array.length,
+ temporaryValue,
+ randomIndex;
+
+ // While there remain elements to shuffle...
+ while (0 !== currentIndex) {
+ // Pick a remaining element...
+ randomIndex = Math.floor(Math.random() * currentIndex);
+ currentIndex -= 1;
+
+ // And swap it with the current element.
+ temporaryValue = array[currentIndex];
+ array[currentIndex] = array[randomIndex];
+ array[randomIndex] = temporaryValue;
+ }
+
+ return array;
+ }
+
+ handleAnswerSelected(event) {
+ this.setState((state, props) => ({
+ disableNextQuestion: false
+ }), () => console.log(this.state));
+ }
+
+ setUserAnswer(answer, correctanswer) {
+ this.setState((state, props) => ({
+ disableNextQuestion: false,
+ myCount: {
+ ...state.myCount,
+ [this.state.counter]: correctanswer
+ },
+ selectedAnswers: {
+ ...state.selectedAnswers,
+ [this.state.counter]: answer
+ },
+ answer: answer
+ }), () => console.log(this.state));
+ }
+
+ checkMultipleAnswers(currentAnswers) {
+ var correctAnswer = 0;
+ var options = quizQuestions[this.state.counter]['answers'].length;
+ for (var index = 0; index < options; index++) {
+ if (quizQuestions[this.state.counter]['answers'][index]['val'] === currentAnswers[index]) {
+ correctAnswer = 1;
+ } else {
+ correctAnswer = 0;
+ break;
+ }
+ }
+
+ this.setUserAnswer(currentAnswers, correctAnswer);
+ }
+
+ getCheckedAnswers() {
+ var checked = document.getElementsByName('checkboxGroup');
+ var currentAnswers = {};
+ for (var index = 0; index < checked.length; index++) {
+ if (checked[index].checked === true) {
+ currentAnswers[index] = 1
+
+ } else {
+ currentAnswers[index] = 0
+ }
+
+ }
+ return currentAnswers;
+ }
+
+ setNextQuestion() {
+ var currentAnswers = this.getCheckedAnswers();
+ console.log("get selected answers:");
+ console.log(currentAnswers);
+
+ //Calculate correct answers and set accordingly
+ this.checkMultipleAnswers(currentAnswers);
+
+ //Set next question
+ if (this.state.questionId < quizQuestions.length) {
+ const counter = this.state.counter + 1;
+ const questionId = this.state.questionId + 1;
+
+ this.setState({
+ counter: counter,
+ questionId: questionId,
+ question: quizQuestions[counter].question,
+ answerOptions: quizQuestions[counter].answers,
+ answer: '',
+ disableNextQuestion: true,
+ multiChoice: quizQuestions[counter].multiChoice
+ });
+ } else {
+ setTimeout(() => this.setResults(this.getResults()), 300);
+ }
+
+ }
+
+ getResults() {
+ const myCount = this.state.myCount;
+ var correct = 0;
+ var total = 0;
+ for (var item in myCount) {
+ total++;
+ if (myCount.hasOwnProperty(item)) {
+ if (myCount[item] === 1) correct++;
+ }
+ }
+ var percent = Math.floor(correct / total * 100);
+ return "" + percent + "%"
+ }
+
+ setResults(result) {
+ this.setState({result: result});
+ }
+
+ renderQuiz() {
+ return (
+
+
+
+ );
+ }
+
+ renderResult() {
+ return (
+ );
+ }
+
+ render() {
+ return (
+
+ {this.state.result ? this.renderResult() : this.renderQuiz()}
+
+ );
+ }
+}
+
+export default App;
diff --git a/frontend/src/quiz/App.test.js b/frontend/src/quiz/App.test.js
new file mode 100644
index 0000000..d1c2db9
--- /dev/null
+++ b/frontend/src/quiz/App.test.js
@@ -0,0 +1,9 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+it('renders without crashing', () => {
+ const div = document.createElement('div');
+ ReactDOM.render(, div);
+ ReactDOM.unmountComponentAtNode(div);
+});
diff --git a/frontend/src/quiz/api/quizQuestions.js b/frontend/src/quiz/api/quizQuestions.js
new file mode 100644
index 0000000..49dd6a1
--- /dev/null
+++ b/frontend/src/quiz/api/quizQuestions.js
@@ -0,0 +1,115 @@
+var quizQuestions = [
+ {
+ question: "Which of the following best describes color contrast?",
+ answers: [
+ {
+ val: 1,
+ content: "A measurement of the difference between two colors when they are layered on top of each other"
+ },
+ {
+ val: 0,
+ content: "A measurement of a color’s brightness"
+ },
+ {
+ val: 0,
+ content: "A condition resulting in reduced sensitivity to green light"
+ },
+ {
+ val: 0,
+ content: "None of the above"
+ }
+
+ ],
+ multiChoice: false
+ },
+ {
+ question: "What is the minimum contrast allowed by the WCAG AAA Color Standards?",
+ answers: [
+ {
+ val: 0,
+ content: "4"
+ },
+ {
+ val: 1,
+ content: "7"
+ },
+ {
+ val: 0,
+ content: "9"
+ },
+ {
+ val: 0,
+ content: "11"
+ }
+ ],
+ multiChoice: false
+ },
+ {
+ question: "Which of the following is the best way to correct an improper ratio in color contrast?",
+ answers: [
+ {
+ val: 0,
+ content: "Do nothing, it’s impossible to fix"
+ },
+ {
+ val: 0,
+ content: "Make everything black and white"
+ },
+ {
+ val: 1,
+ content: "Use a color contrast calculator"
+ },
+ {
+ val: 0,
+ content: "None of the above"
+ }
+ ],
+ multiChoice: false
+ },
+ {
+ question: "Which of the following is NOT a form of color vision deficiency?\n",
+ answers: [
+ {
+ val: 0,
+ content: "Protanopia"
+ },
+ {
+ val: 0,
+ content: "Deuteranopia"
+ },
+ {
+ val: 0,
+ content: "Tritanopia"
+ },
+ {
+ val: 1,
+ content: "Dichrompia"
+ }
+ ],
+ multiChoice: false
+ },
+ {
+ question: "If placed in a color contrast calculator, which of the following would result in a proper color contrast ratio?",
+ answers: [
+ {
+ val: 0,
+ content: "Dark red placed on another shade of dark red"
+ },
+ {
+ val: 0,
+ content: "White placed on light pink"
+ },
+ {
+ val: 1,
+ content: "Light pink placed on black"
+ },
+ {
+ val: 0,
+ content: "Black placed on black"
+ }
+ ],
+ multiChoice: false
+ }
+];
+
+export default quizQuestions;
diff --git a/frontend/src/quiz/components/AnswerOption.js b/frontend/src/quiz/components/AnswerOption.js
new file mode 100644
index 0000000..57b4d7d
--- /dev/null
+++ b/frontend/src/quiz/components/AnswerOption.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+function AnswerOption(props) {
+ if(props.multiChoice == true){
+ return (
+
+
+
+
+ );
+ }
+ else {
+ return (
+
+
+
+
+ );
+ }
+}
+
+AnswerOption.propTypes = {
+ answerType: PropTypes.string.isRequired,
+ answerContent: PropTypes.string.isRequired,
+ onAnswerSelected: PropTypes.func.isRequired,
+ multiChoice: PropTypes.func.isRequired
+};
+
+export default AnswerOption;
\ No newline at end of file
diff --git a/frontend/src/quiz/components/Certificate.js b/frontend/src/quiz/components/Certificate.js
new file mode 100644
index 0000000..db9e7c4
--- /dev/null
+++ b/frontend/src/quiz/components/Certificate.js
@@ -0,0 +1,83 @@
+import React, {Component} from "react";
+import "./../../vendor/bootstrap/css/bootstrap.min.css";
+import "./../../css/agency.min.css";
+import "./../../css/style.css";
+import {connect} from "react-redux";
+import {actions as appActions} from '../../reducers/AppReducer';
+import {bindActionCreators} from 'redux';
+import logo from "./../../img/accessCycle.png";
+
+const mapStateToProps = (state) => {
+ return {
+ // General
+ state: state
+ };
+};
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators(appActions, dispatch)
+});
+
+
+
+class Certificate extends Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+
+ };
+
+
+ }
+ getColor=()=>{
+ let score = this.props.quizResult;
+ score = parseFloat(score);
+ switch (true) {
+ case score<=40:
+ return "red";
+ case score<=70:
+ return "orange";
+ default:
+ return "green";
+ }
+ };
+
+ render() {
+ console.log(this.props);
+ const {state, actions} = this.props;
+ var today = new Date();
+ var date = (today.getMonth()+1)+'/'+today.getDate() +'/'+ today.getFullYear();
+ return (
+
+
+
+ Certificate of Completion
+
+
+
This is to certify that you have completed the course:
+
+ {/*
Sakshi*/}
+ {/*
have completed the course */}
+
Accessibility Learning Lab 1: Accessibility to Sound and Speech
+
with score of {this.props.quizResult}
+
dated
+
{date}
+ {/*

*/}
+ {/* */}
+
+

+
+
+
+
+ );
+
+ };
+}
+
+export default connect(
+ mapStateToProps, mapDispatchToProps
+)(Certificate);
diff --git a/frontend/src/quiz/components/Question.js b/frontend/src/quiz/components/Question.js
new file mode 100644
index 0000000..1f3fa87
--- /dev/null
+++ b/frontend/src/quiz/components/Question.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+function Question(props) {
+ return {props.content}
;
+}
+
+Question.propTypes = {
+ content: PropTypes.string.isRequired
+};
+
+export default Question;
diff --git a/frontend/src/quiz/components/QuestionCount.js b/frontend/src/quiz/components/QuestionCount.js
new file mode 100644
index 0000000..7bb4d50
--- /dev/null
+++ b/frontend/src/quiz/components/QuestionCount.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+function QuestionCount(props) {
+ return (
+
+ Question {props.counter} of {props.total}
+
+ );
+}
+
+QuestionCount.propTypes = {
+ counter: PropTypes.number.isRequired,
+ total: PropTypes.number.isRequired
+};
+
+export default QuestionCount;
diff --git a/frontend/src/quiz/components/Quiz.js b/frontend/src/quiz/components/Quiz.js
new file mode 100644
index 0000000..b065fb8
--- /dev/null
+++ b/frontend/src/quiz/components/Quiz.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Question from '../components/Question';
+import QuestionCount from '../components/QuestionCount';
+import AnswerOption from '../components/AnswerOption';
+
+function Quiz(props) {
+ function renderAnswerOptions(key) {
+ return (
+
+ );
+ }
+
+
+ return (
+
+
+
+
+ {props.answerOptions.map(renderAnswerOptions)}
+
+
+
+
+
+ );
+}
+
+
+Quiz.propTypes = {
+ answer: PropTypes.string.isRequired,
+ answerOptions: PropTypes.array.isRequired,
+ question: PropTypes.string.isRequired,
+ questionId: PropTypes.number.isRequired,
+ questionTotal: PropTypes.number.isRequired,
+ onAnswerSelected: PropTypes.func.isRequired,
+ multiChoice: PropTypes.func.isRequired
+};
+
+export default Quiz;
\ No newline at end of file
diff --git a/frontend/src/quiz/components/Result.js b/frontend/src/quiz/components/Result.js
new file mode 100644
index 0000000..3ccfde5
--- /dev/null
+++ b/frontend/src/quiz/components/Result.js
@@ -0,0 +1,124 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import AnswerOption from "./AnswerOption";
+import quizQuestions from "../api/quizQuestions";
+import Certificate from "./Certificate";
+
+function Result(props) {
+
+ function renderTableHeader() {
+ let header = Object.keys(quizQuestions[0])
+ return header.map((key, index) => {
+ return {key.toUpperCase()} |
+ })
+ }
+
+ function isAnswerIncorrect(score) {
+ if (score == 1) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ function renderTableData() {
+ var counter = 0;
+ var isIncorrect = false;
+ return quizQuestions.map((quizQuestion, index) => {
+ const {question, answers} = quizQuestion //destructuring
+ counter += 1;
+ isIncorrect = isAnswerIncorrect(props.quizScore[counter - 1]);
+ return (
+
+ | {question} |
+ {renderTableAnswersData(answers)} |
+ {renderTableSelectedAnswersData(props.selectedAnswers[counter - 1], answers)} |
+
+
+ );
+
+ })
+
+ }
+
+ function renderTableAnswersData(answers) {
+ var counter = 0;
+ return (
+
+ {answers.map(function (answer, index) {
+ counter += 1;
+ if (answer['val'] == 1) {
+ return (
+ - {counter}. {answer['content']}
+
+
+ );
+ }
+
+ })}
+
+ )
+ }
+
+
+ function renderTableSelectedAnswersData(selectedAnswers, answers) {
+ const choices = Object.values(selectedAnswers);
+ var counter = 0;
+ return (
+
+ {choices.map(function (selectedAnswer, index) {
+ counter += 1;
+ if (selectedAnswer == 1) {
+ return (
+ - {counter}. {answers[counter - 1]['content']}
+
+
+
+ );
+ }
+
+
+ })}
+
+ )
+
+ }
+
+
+
+ return (
+
+
+
+ Results
Score: {props.quizResult}
+
+
+
+
+
+ {/*{renderTableHeader()}*/}
+ | QUESTION |
+ ANSWERS |
+ SELECTED ANSWERS |
+
+ {renderTableData()}
+
+
+
+
+
+
+
+
+ );
+}
+
+
+Result.propTypes = {
+ quizResult: PropTypes.string.isRequired,
+ quizScore: PropTypes.string.isRequired,
+ selectedAnswers: PropTypes.string.isRequired
+};
+
+export default Result;
diff --git a/frontend/src/quiz/index.css b/frontend/src/quiz/index.css
new file mode 100644
index 0000000..b2db352
--- /dev/null
+++ b/frontend/src/quiz/index.css
@@ -0,0 +1,148 @@
+body {
+ margin: 0;
+ padding: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ background: #eee;
+ font-family: sans-serif;
+}
+
+.container {
+ position: relative;
+ max-width: 800px;
+ margin: 0 auto;
+ background: #fff;
+ border-radius: 3px;
+}
+
+@media (min-width: 769px) {
+ .container {
+ margin: 2.5rem auto;
+ }
+}
+
+/* Result Component */
+.result {
+ padding: 1.5rem 2.5rem;
+}
+
+/* Quiz Component */
+.answerOptions {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* QuestionCount Component */
+.questionCount {
+ padding: 1.5rem 2.5rem 0;
+ font-size: 14px;
+}
+
+/* Question Component */
+.question {
+ margin: 0;
+ padding: 0.5rem 2.5rem 1.5rem 2.5rem;
+}
+
+/* AnswerOption Component */
+.answerOption {
+ border-top: 1px solid #eee;
+}
+
+.answerOption:hover {
+ background-color: #eefbfe;
+}
+
+.radioCustomButton {
+ position: absolute;
+ width: auto;
+ opacity: 0;
+}
+
+.radioCustomButton,
+.radioCustomLabel {
+ display: inline-block;
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.radioCustomLabel {
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 1.5rem 2.5rem 1.5rem 5rem;
+ font-size: 16px;
+ line-height: 1.5;
+}
+
+.radioCustomButton ~ .radioCustomLabel:before {
+ position: absolute;
+ top: 20px;
+ left: 38px;
+ width: 28px;
+ height: 28px;
+ content: '';
+ display: inline-block;
+ vertical-align: middle;
+ background: #fff;
+ border: 1px solid #bbb;
+ border-radius: 50%;
+ transition: all 0.3s;
+}
+
+.radioCustomButton:checked ~ .radioCustomLabel:before {
+ content: '';
+ background: #8bc53f no-repeat;
+ background-size: 27px;
+ border-color: #8bc53f;
+}
+
+/* Animation */
+.fade-enter {
+ opacity: 0;
+}
+
+.fade-enter.fade-enter-active {
+ opacity: 1;
+ transition: opacity 0.5s ease-in-out 0.3s;
+}
+
+.fade-leave {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ opacity: 1;
+}
+
+.fade-leave.fade-leave-active {
+ opacity: 0;
+ transition: opacity 0.5s ease-in-out;
+}
+
+.fade-appear {
+ opacity: 0;
+}
+
+.fade-appear.fade-appear-active {
+ opacity: 1;
+ transition: opacity 0.5s ease-in-out;
+}
diff --git a/frontend/src/quiz/index.js b/frontend/src/quiz/index.js
new file mode 100644
index 0000000..0c5e75d
--- /dev/null
+++ b/frontend/src/quiz/index.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+import * as serviceWorker from './serviceWorker';
+
+ReactDOM.render(, document.getElementById('root'));
+
+// If you want your app to work offline and load faster, you can change
+// unregister() to register() below. Note this comes with some pitfalls.
+// Learn more about service workers: http://bit.ly/CRA-PWA
+serviceWorker.unregister();
diff --git a/frontend/src/quiz/serviceWorker.js b/frontend/src/quiz/serviceWorker.js
new file mode 100644
index 0000000..ec7e8fb
--- /dev/null
+++ b/frontend/src/quiz/serviceWorker.js
@@ -0,0 +1,127 @@
+// In production, we register a service worker to serve assets from local cache.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on the "N+1" visit to a page, since previously
+// cached resources are updated in the background.
+
+// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
+// This link also includes instructions on opting out of this behavior.
+
+const isLocalhost = Boolean(
+ window.location.hostname === 'localhost' ||
+ // [::1] is the IPv6 localhost address.
+ window.location.hostname === '[::1]' ||
+ // 127.0.0.1/8 is considered localhost for IPv4.
+ window.location.hostname.match(
+ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+ )
+);
+
+export function register(config) {
+ if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+ // The URL constructor is available in all browsers that support SW.
+ const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
+ if (publicUrl.origin !== window.location.origin) {
+ // Our service worker won't work if PUBLIC_URL is on a different origin
+ // from what our page is served on. This might happen if a CDN is used to
+ // serve assets; see https://github.com/facebook/create-react-app/issues/2374
+ return;
+ }
+
+ window.addEventListener('load', () => {
+ const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+ if (isLocalhost) {
+ // This is running on localhost. Let's check if a service worker still exists or not.
+ checkValidServiceWorker(swUrl, config);
+
+ // Add some additional logging to localhost, pointing developers to the
+ // service worker/PWA documentation.
+ navigator.serviceWorker.ready.then(() => {
+ console.log(
+ 'This web app is being served cache-first by a service ' +
+ 'worker. To learn more, visit https://goo.gl/SC7cgQ'
+ );
+ });
+ } else {
+ // Is not local host. Just register service worker
+ registerValidSW(swUrl, config);
+ }
+ });
+ }
+}
+
+function registerValidSW(swUrl, config) {
+ navigator.serviceWorker
+ .register(swUrl)
+ .then(registration => {
+ registration.onupdatefound = () => {
+ const installingWorker = registration.installing;
+ installingWorker.onstatechange = () => {
+ if (installingWorker.state === 'installed') {
+ if (navigator.serviceWorker.controller) {
+ // At this point, the old content will have been purged and
+ // the fresh content will have been added to the cache.
+ // It's the perfect time to display a "New content is
+ // available; please refresh." message in your web app.
+ console.log('New content is available; please refresh.');
+
+ // Execute callback
+ if (config.onUpdate) {
+ config.onUpdate(registration);
+ }
+ } else {
+ // At this point, everything has been precached.
+ // It's the perfect time to display a
+ // "Content is cached for offline use." message.
+ console.log('Content is cached for offline use.');
+
+ // Execute callback
+ if (config.onSuccess) {
+ config.onSuccess(registration);
+ }
+ }
+ }
+ };
+ };
+ })
+ .catch(error => {
+ console.error('Error during service worker registration:', error);
+ });
+}
+
+function checkValidServiceWorker(swUrl, config) {
+ // Check if the service worker can be found. If it can't reload the page.
+ fetch(swUrl)
+ .then(response => {
+ // Ensure service worker exists, and that we really are getting a JS file.
+ if (
+ response.status === 404 ||
+ response.headers.get('content-type').indexOf('javascript') === -1
+ ) {
+ // No service worker found. Probably a different app. Reload the page.
+ navigator.serviceWorker.ready.then(registration => {
+ registration.unregister().then(() => {
+ window.location.reload();
+ });
+ });
+ } else {
+ // Service worker found. Proceed as normal.
+ registerValidSW(swUrl, config);
+ }
+ })
+ .catch(() => {
+ console.log(
+ 'No internet connection found. App is running in offline mode.'
+ );
+ });
+}
+
+export function unregister() {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then(registration => {
+ registration.unregister();
+ });
+ }
+}
diff --git a/frontend/src/quiz/svg/icon-check.svg b/frontend/src/quiz/svg/icon-check.svg
new file mode 100644
index 0000000..36840b5
--- /dev/null
+++ b/frontend/src/quiz/svg/icon-check.svg
@@ -0,0 +1,4 @@
+
diff --git a/frontend/src/quiz/svg/logo.svg b/frontend/src/quiz/svg/logo.svg
new file mode 100644
index 0000000..467e1dc
--- /dev/null
+++ b/frontend/src/quiz/svg/logo.svg
@@ -0,0 +1,35 @@
+
+
+
+