Skip to content
Snippets Groups Projects
Commit eaafaf6f authored by Torbjørn Antonsen's avatar Torbjørn Antonsen
Browse files

Merge branch 'frontend-playquiz-functionality' into 'main'

fixed logic to make frontend and backend speak together

See merge request !42
parents 8bae8c22 6958d8db
Branches
No related tags found
1 merge request!42fixed logic to make frontend and backend speak together
Pipeline #269831 passed
Showing
with 597 additions and 19 deletions
......@@ -16,7 +16,7 @@ export default {
intervalId = setInterval(async () => {
console.log("Attempting to refresh token...");
await refreshToken();
}, 10000); // Refresh token every 5 minutes (300000 ms)
}, 300000); // Refresh token every 5 minutes (300000 ms)
}
};
......
......@@ -85,6 +85,7 @@ export default {
<template>
<div class="modal-overlay" @click="closeModal">
<div @click.stop class="modal-mask">
<div class="modal-container">
<form @submit.prevent="handleSubmit">
<div class="question-title">
......
<script>
import {apiClient} from "@/api.js";
export default {
props: {
question: Object,
selectedOption: String,
},
data() {
return {
questionText: null,
options: [],
correctAnswer: null,
}
},
mounted() {
//this.getSampleQuestion();
},
methods: {
getSampleQuestion() {
this.question = {
id: 3,
questionText: "question 3",
options: ["ans1","ans2"],
answer: "ans2"
}
},
getQuestion(questionId) {
try {
apiClient.get('/questions/question/' + this.questionId).then(response => {
this.question = JSON.parse(response.data);
this.questionText = response.data.questionText;
this.options = JSON.parse(response.data.options);
this.correctAnswer = response.data.answer;
});
} catch (error) {
//TODO: proper error handling
this.errorMsg = 'Error retrieving question';
}
},
selectOption(option) {
this.$emit('update:selectedOption', option);
}
}
}
</script>
<template>
<div class="quiz">
<div id="info">
<div id="score">Score: 0</div>
<div id="ques-left">Question:1/20</div>
</div>
<div id="ques-view">
</div>
<div class="question">
<h1>Q title</h1>
</div>
<ul v-if="question.options">
<li v-for="(option) in question.options" :key="option">
<!--
<input type="radio" :id="'option'" :value="option"
:checked="option === selectedOption" @change="selectOption(option)">
-->
<label :for="option">option label</label>
</li>
</ul>
<!--
<div class="choice">
<div class="options"><input type="radio" name="options" value="option1" id="opt0"><label for="opt0" id="lb0">Option1</label></div>
<div class="options"><input type="radio" name="options" value="option2" id="opt1"><label for="opt1" id="lb1">Option2</label></div>
<div class="options"><input type="radio" name="options" value="option3" checked="checked" id="opt2"><label for="opt2" id="lb2">Option3</label></div>
<div class="options"><input type="radio" name="options" value="option4" id="opt3"><label for="opt3" id="lb3">Option4</label></div>
</div>
<div class="ans-btn">
<button type="button" class="submit-answer">Submit Answer</button>
<a href="#display-final-score" type="button" class="view-results">View Results</a>
</div>
-->
</div>
</template>
<style scoped>
.quiz{
text-align: center;
margin-top: 20px;
height: 100vh;
}
#info{
height:25px;
}
#score{
width:50%;
float:left;
font-size: 25px;
}
#ques-left{
width:50%;
float:left;
font-size:25px;
}
#ques-view{
height: 35px;
margin-top: 10px;
padding: 2px;
}
.question{
letter-spacing: .13em;
}
.choice{
padding: 3%;
}
.options{
display:block;
font-size: 25px;
margin-top: 30px;
text-align: left;
}
input[type=radio] {
border: 5px solid white;
width: 20px;
height: 1.3em;
float: left;
}
.ans-btn{
padding: 2%;
}
.submit-answer{
border: 2px solid #CCA43B;
padding: 15px;
border-radius: 20px;
transition: background-color 0.3s,border 0.2s, color 0.2s;
margin-right: 10px;
font-family: monospace;
}
.submit-answer:hover{
background-color: #CCA43B;
padding: 16px;
color: #fff;
cursor: pointer;
border-radius: 20px;
}
.view-results{
text-decoration: none;
color: black;
border: 2px solid #CCA43B;
padding: 15px;
border-radius: 20px;
transition: background-color 0.3s,border 0.2s, color 0.2s;
}
.view-results:hover{
background-color: #CCA43B;
padding: 16px;
color: #fff;
cursor: pointer;
border-radius: 20px;
}
@media only screen and (max-width: 1250px) {
.options{
margin-left: 8%
}
}
@media only screen and (max-width: 850px) {
.options{
margin-left: 4%;
}
}
@media only screen and (max-width: 650px) {
.options{
display: block;
margin-top: 3%;
}
}
@media only screen and (max-width: 550px) {
.options{
margin-top: 3%;
}
}
</style>
\ No newline at end of file
<script>
export default {
name: "QuizResult"
}
</script>
<template>
<div class="final-result">
<h1>The Quiz is Over</h1>
<div class="solved-ques-no">You Solved 10 questions of {Name of quiz here}</div>
<div class="right-wrong">3/4 were right</div>
<div id="display-final-score">Your Final Score is: 35</div>
<div class="remark">Remark: Satisfactory, Keep trying!</div>
<button id="restart">Restart Quiz</button>
</div>
</template>
<style scoped>
/*Final Results*/
.final-result{
text-align: center;
padding: 10px;
font-size: 1.5em;
height: 100vh;
}
.solved-ques-no{
padding: 10px;
}
.right-wrong{
padding: 10px;
}
#display-final-score{
padding: 5%;
}
.remark{
padding: 5%
}
#restart{
background-color:#CCA43B;
margin-left: 30px;
border: 2px solid #CCA43B;
padding: 15px;
border-radius: 20px;
font-size: 80%;
transition: background-color 0.3s,border 0.2s, color 0.2s;
}
#restart:hover{
background-color: #CCA43B;
color:#fff;
cursor: pointer;
/* width: 120px;*/
font-size: 90%;
}
@media only screen and (max-width: 1250px) {
.options{
margin-left: 8%
}
}
@media only screen and (max-width: 850px) {
.options{
margin-left: 4%;
}
}
@media only screen and (max-width: 650px) {
.options{
display: block;
margin-top: 3%;
}
}
@media only screen and (max-width: 550px) {
.options{
margin-top: 3%;
}
}
</style>
\ No newline at end of file
......@@ -46,9 +46,9 @@ export default {
getQuiz(quizId) {
console.log('Fetching data for quiz: ', quizId);
try {
apiClient.get('/questions/allQuestionsToAQuiz/' + this.quizId).then(response => {
apiClient.get('/quiz/quiz/' + this.quizId).then(response => {
console.log(response);
this.quizTitle = JSON.parse(response.data.title);
this.quizTitle = response.data.title;
this.questions = response.data.questions;
this.creatorId = JSON.parse(response.data.creatorId);
this.category = response.data.category;
......
export default class Quiz {
constructor(quizId, title, creatorId, questions, category, difficulty) {
constructor(quizId, title, creatorId, category, difficulty) {
this.quizId = quizId;
this.title = title;
this.creatorId = creatorId;
this.questions = questions;
this.category = category;
this.difficulty = difficulty;
}
}
/**
......
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import EditQuizView from "@/components/shared/create-quiz/EditQuizView.vue";
import PlayQuizView from "@/views/PlayQuizView.vue";
import CreateQuizView from "@/components/shared/create-quiz/CreateQuizView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
......@@ -35,20 +37,20 @@ const router = createRouter({
name: 'signup',
component: () => import('../views/SignupView.vue')
},
{
path: '/createQuiz',
name: 'create Quiz',
component: CreateQuizView
},
{
path: '/overviewQuiz',
name: 'overview Quiz',
component: () => import('../views/OverviewQuizView.vue')
},
{
path: '/create-quiz',
name: 'create Quiz',
component: () => import('../components/shared/create-quiz/CreateQuizView.vue')
},
{
path: '/play-quiz/:quizId',
name: 'playQuiz',
component: () => import('../views/PlayQuizView.vue'),
component: PlayQuizView,
params: true
},
{
......@@ -61,7 +63,7 @@ const router = createRouter({
path: '/profile',
name: 'profile',
component: () => import('../views/ProfileView.vue')
},
}
]
})
......
<script>
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import router from "@/router/index.js";
import {apiClient} from "@/api.js";
import {getIdByToken} from "@/tokenController.js";
import {categoryEnums} from "@/data/categories.js"
import {difficultyEnums} from "@/data/difficulties.js";
//like editquiz, but w/o questions, redirect to edit when quiz is constructed
export default {
data() {
return {
showNewQuestion: false,
creatorId: null,
quiz: null,
quizId: null,
quizTitle: '',
questions: [],
category: '',
difficulty: '',
errorMsg: '',
selectedCategory: null,
categories: categoryEnums,
selectedDifficulty: null,
difficulties: difficultyEnums,
//TODO: make quiz object
};
},
mounted() {
this.getUser();
},
methods: {
async constructQuiz() {
try {
await apiClient.post('quiz/create', {
title: this.quizTitle,
questionIds: this.questions,
creatorId: this.creatorId,
category: this.selectedCategory,
difficulty: this.selectedDifficulty
}).then(response => {
this.quizId = JSON.parse(response.data.id);
router.push({name: 'editQuiz', params: {quizId: this.quizId}});
})
} catch(error){
this.errorMsg = 'Cannot construct quiz';
}
},
getUser() {
this.creatorId = getIdByToken();
}
},
}
</script>
<template>
<body>
<div class="new-quiz-page">
<form @submit.prevent="constructQuiz">
<router-link to="/overviewQuiz"> <- </router-link>
<h1>New quiz</h1>
<div>
<h2>Title</h2>
<input>
</div>
<div>
<h2>Category</h2>
<form>
<select v-model="selectedCategory">
<option v-for="category in categories" :key="category.id" :value="category">{{categories.category}}</option>
</select>
</form>
</div>
<div>
<h2>Difficulty</h2>
<form>
<select v-model="selectedDifficulty">
<option v-for="difficulty in difficulties" :key="difficulty.id" :value="difficulty">{{difficulties.difficulty}}</option>
</select>
</form>
</div>
<div class="footer">
<router-link to="/overviewQuiz" class="delete-btn"> Cancel </router-link>
<button class="submit-btn" type="submit"> Submit</button>
</div>
</form>
</div>
</body>
</template>
<style>
.new-quiz-page{
margin:20vh;
}
input{
height: 25px;
width: 100%;
}
select{
min-width: 100%;
height: 25px;
background-color: #E5E5E5;
border-color: transparent;
border-radius: 5px;
}
.footer{
margin: 10vh;
display: flex;
justify-content: center;
align-items: center;
}
</style>
\ No newline at end of file
......@@ -8,7 +8,7 @@
<h1>Your quizzes</h1>
<p>Select a quiz for your creation to either play, edit or delete</p>
</div>
<router-link to="/create-quiz" class="add-Btn">New quiz</router-link>
<router-link to="/createQuiz" class="add-Btn">New quiz</router-link>
</div>
<div class="row">
......
<script>
import QuizResult from "@/components/shared/PlayQuiz/QuizResult.vue";
import {getIdByToken} from "@/tokenController.js";
import {apiClient} from "@/api.js";
export default {
data() {
return {
currentQuestion: null,
quizResult: null,
currentQuestionIndex: 0,//set to null
userId: 0, //set to null
quizId: 0, //set to null
quizTitle: 'Quiz title',
selectedOption: '',
questions: [],
currentQuestionText: '',
currentQuestionId: null,
currentAnswers: [],
quizResultId: 0,
errorMsg: '',
}
},
beforeMount() {
this.getUser();
},
async mounted() {
await this.getUser();
await this.setup();
this.getQuestions();
this.getQuiz();
this.setCurrentQuestion();
},
methods: {
async setup(){
//TODO: QuizResultService
this.quizId = this.$route.params.quizId;
const data = {
quizId: this.quizId,
userId: this.userId
}
console.log(data)
try {
await apiClient.post('/results/create', data)//TODO: set QuizResultServiceId
} catch (error) {
this.errorMsg = 'Error starting quiz';
}
},
async getUser() {
this.userId = await getIdByToken();
},
getQuiz() {
try {
apiClient.get('/quiz/quiz/' + this.quizId).then(response => {
this.quizTitle = response.data.questions;
});
} catch (error) {
this.errorMsg = 'Error retrieving quiz';
}
},
getQuestions(quizId) {
//TODO: use method to fetch questions
try {
apiClient.get('/questions/allQuestionsToAQuiz/' + this.quizId).then(response => {
this.questionIds = response.data;
});
console.log(this.questionIds[0])
} catch (error) {
this.errorMsg = 'Error retrieving questions';
}
},
getSampleQuestions() {
const quest = {
id: 0,
questionText: "question " + -1,
options: ["ans1","ans2", "ansThree"],
}
this.questions.push(quest);
for(let i=0; i<4; i++){
const question = {
id: i,
questionText: "question " + i,
options: ["ans1","ans2"]
}
this.questions.push(question);
console.log(question.id);
console.log(this.questions.length);
}
},
async nextQuestion() {
if(!this.selectedOption) {
this.errorMsg = 'You must select an answer';
} else {
this.errorMsg = '';
}
console.log(this.selectedOption);
console.log(this.selectedOption);
try {
const data = {
id: this.quizId,
questionId: this.currentQuestionId,
givenAnswer: this.selectedOption,
}
console.log(data)
await apiClient.post('/questions/save', data)
} catch (error) {
this.errorMsg = 'Cannot submit question';
}
this.currentQuestionIndex++
this.setCurrentQuestion();
},
//setting current question, updating info
setCurrentQuestion() {
/*if(!this.questions.empty()){
this.currentQuestion = this.questions[this.currentQuestionIndex];
}*/ //for some reason this check messes up the app???
this.currentQuestion = this.questions[this.currentQuestionIndex];
//this.currentQuestionId = this.currentQuestion.id;
//this.currentQuestionText = this.currentQuestion.questionText;
//this.currentAnswers = this.currentQuestion.options;
},
setupMCQuestion(question) {
this.questionTitle = question.questionText;
this.currentAnswers = question.options;
this.currentCorrectAnswer = question.answer;
},
selectOption(option) {
this.selectedOption = option;
console.log(this.selectedOption);
}
}
}
</script>
<template>
<div class="quiz">
<div id="quiz-info">
<h1 id="title">Play quiz</h1>
</div>
<div id="current-q" class="question">
<h3 v-if="currentQuestion">{{currentQuestionText}}</h3>
<ul>
<li v-for="(option, index) in currentAnswers" :key="index">
<label>{{ option }}</label>
<input type="radio" :id="option" :value="option" :checked="option === selectedOption" @change="selectOption(option)">
</li>
<!--
<li v-for="option in answersList">
<label>test label</label>-->
<!--
<input type="radio" :id="'option'" :value="option"
:checked="option === selectedOption">-->
<!--<label :for="'option'">option</label>-->
</ul>
<button @click="nextQuestion">Next Question</button>
</div>
</div>
<!--
<body>
<div class="quiz">
......@@ -43,7 +206,7 @@
</div>
</body>
-->
</template>
<style>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment