Skip to content
Snippets Groups Projects
Commit 96ba7009 authored by Kristiane Skogvang Kolshus's avatar Kristiane Skogvang Kolshus
Browse files

Begin functionality for quiz creation

parent a76be276
No related branches found
No related tags found
1 merge request!23Frontend quiz api req
Showing with 580 additions and 113 deletions
<script>
import { RouterLink, RouterView } from 'vue-router';
import router from "@/router/index.js";
import { useRouter } from 'vue-router';
export default {
props: {
answerId: {
type: Number,
required: true,
},
answer: {
type: String,
required: true,
},
correct: {
type: Boolean,
required: false
}
},
methods: {
deleteAnswer() {
}
}
}
</script>
<template>
<div class="question-wrapper">
<h3>{{answer}}</h3>
<div class="quiz-footer">
<button @click="deleteAnswer" class="delete-btn">Delete</button>
</div>
</div>
</template>
<style>
.overViewQuestion-page{
padding: 50px;
}
.quiz-header{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.quiz-body{
margin-bottom: 40px;
}
.question-wrapper {
display: flex;
width: 80%;
justify-content: space-between;
}
.quiz-footer{
display: flex;
justify-content: space-evenly;
align-items: center;
padding-left: 25px;
padding-right: 25px;
}
</style>
\ No newline at end of file
<script>
/*
<script setup>
const props = defineProps({
show: Boolean
})*/
import AnswerCard from "@/components/shared/AnswerCard.vue";
export default {
components: {AnswerCard},
data() {
return {
questionText: '',
answers: [
{answerId: 0, answer: 'first answer', correct: true},
{answerId: 1, answer: 'second answer', correct: false},
{answerId: 2, answer: 'third answer', correct: false},
],
correctIndex: 0
}
},
methods: {
closeModal() {
this.$emit('close');
},
newAnswer() {
//default: not correct!
}
}
};
</script>
<template>
<div class="modal-overlay" @click="closeModal">
<div @click.stop class="modal-mask">
<div class="modal-container">
<div class="question-title">
<h3>Question:</h3>
<input v-model="questionText" placeholder="Type your question here">
</div>
<div class="modal-body">
<AnswerCard answer-id="answerCard" v-for="answer in answers"
:key="answer.id" :answerId="answer.id" :answer="answer.answer" :correct="answer.correct"/>
</div>
<div class="modal-footer">
default footer
<button
class="modal-default-button"
@click="$emit('close')">
OK
</button>
</div>
</div>
</div>
</div>
<!--
<Transition name="modal">
<div v-if="show" class="modal-mask">
<div class="modal-container">
<div class="modal-header">
<slot name="header">default header</slot>
</div>
<div class="modal-body">
<slot name="body">default body</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button
class="modal-default-button"
@click="$emit('close')"
>OK</button>
</slot>
</div>
</div>
</div>
</Transition>
-->
</template>
<style>
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
transition: opacity 0.3s ease;
}
.modal-container {
width: 500px;
margin: auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
}
.question-title {
display: flex;
justify-content: space-between;
}
.modal-header h5 {
margin-top: 0;
color: #363636;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
</style>
\ No newline at end of file
<script>
/*
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
show: Boolean show: Boolean
}) })*/
import AnswerCard from "@/components/shared/AnswerCard.vue";
export default {
components: {AnswerCard},
data() {
return {
questionText: '',
answers: [
{answerId: 0, answer: 'first answer', correct: true},
{answerId: 1, answer: 'second answer', correct: false},
{answerId: 2, answer: 'third answer', correct: false},
],
correctIndex: 0
}
},
methods: {
closeModal() {
this.$emit('close');
},
newAnswer() {
//default: not correct!
}
}
};
</script> </script>
<template> <template>
<div class="modal-overlay" @click="closeModal">
<div @click.stop class="modal-mask">
<div class="modal-container">
<div class="question-title">
<h3>Question:</h3>
<input v-model="questionText" placeholder="Type your question here">
</div>
<div class="modal-body">
<AnswerCard answer-id="answerCard" v-for="answer in answers"
:key="answer.id" :answerId="answer.id" :answer="answer.answer" :correct="answer.correct"/>
</div>
<div class="modal-footer">
default footer
<button
class="modal-default-button"
@click="$emit('close')">
OK
</button>
</div>
</div>
</div>
</div>
<!--
<Transition name="modal"> <Transition name="modal">
<div v-if="show" class="modal-mask"> <div v-if="show" class="modal-mask">
<div class="modal-container"> <div class="modal-container">
...@@ -28,6 +78,7 @@ const props = defineProps({ ...@@ -28,6 +78,7 @@ const props = defineProps({
</div> </div>
</div> </div>
</Transition> </Transition>
-->
</template> </template>
<style> <style>
...@@ -53,6 +104,14 @@ const props = defineProps({ ...@@ -53,6 +104,14 @@ const props = defineProps({
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.question-title {
display: flex;
justify-content: space-between;
}
.modal-header h5 { .modal-header h5 {
margin-top: 0; margin-top: 0;
color: #363636; color: #363636;
......
<script>
import { RouterLink, RouterView } from 'vue-router';
import router from "@/router/index.js";
import { useRouter } from 'vue-router';
export default {
props: {
questionId: {
type: Number,
required: true,
},
questionNum: {
type: Number,
required: false,
},
question: {
type: String,
required: false,
},
},
methods: {
//link to pages, play quiz, edit, delete, with quizId
viewQuestion() {
//create new router-method to playQuiz, using quizId
//router.push({name: 'playQuiz', params: {questionId: this.questionId}});
},
editQuestion() {
//create new router-method to editQuiz, using quizId
//this.$router.push({name: 'editQuiz', params: {questionId: this.questionId}});
},
deleteQuestion() {
}
}
}
</script>
<template>
<div class="question-wrapper">
<h4>{{questionNum}}</h4>
<h3>{{question}}</h3>
<div class="quiz-footer">
<button @click="viewQuestion" class="play-btn">Play</button>
<button @click="editQuestion" class="edit-btn">Edit</button>
<button @click="deleteQuestion" class="delete-btn">Edit</button>
</div>
</div>
</template>
<style>
.overViewQuestion-page{
padding: 50px;
}
.quiz-header{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.quiz-body{
margin-bottom: 40px;
}
.question-wrapper {
display: flex;
width: 80%;
justify-content: space-between;
}
.quiz-footer{
display: flex;
justify-content: space-evenly;
align-items: center;
padding-left: 25px;
padding-right: 25px;
}
</style>
\ No newline at end of file
<script>
import { RouterLink, RouterView } from 'vue-router';
import router from "@/router/index.js";
import { useRouter } from 'vue-router';
export default {
props: {
quizId: {
type: Number,
required: true,
},
quizName: {
type: String,
required: true,
},
quizDescription: {
type: String,
required: true,
}
},
methods: {
//link to pages, play quiz, edit, delete, with quizId
playQuiz() {
//create new router-method to playQuiz, using quizId
router.push({name: 'playQuiz', params: {quizId: this.quizId}});
},
editQuiz() {
//create new router-method to editQuiz, using quizId
this.$router.push({name: 'editQuiz', params: {quizId: this.quizId}});
},
}
}
</script>
<template>
<div class="course-col">
<div class="quiz-header">
<h3>{{ quizName }}</h3>
<!--
<Svg :name="selectedIcon" />
-->
</div>
<div class="quiz-body">
<p>{{ quizDescription }}</p>
</div>
<div class="quiz-footer">
<button @click="playQuiz" class="play-btn">Play</button>
<button @click="editQuiz" class="edit-btn">Edit</button>
</div>
</div>
</template>
<style>
.overViewQuestion-page{
padding: 50px;
}
.quiz-header{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.quiz-body{
margin-bottom: 40px;
}
.quiz-footer{
display: flex;
justify-content: space-evenly;
align-items: center;
padding-left: 25px;
padding-right: 25px;
}
</style>
\ No newline at end of file
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue' import HomeView from '../views/HomeView.vue'
import EditQuizView from "@/views/EditQuizView.vue";
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
...@@ -45,9 +46,16 @@ const router = createRouter({ ...@@ -45,9 +46,16 @@ const router = createRouter({
component: () => import('../views/OverviewQuizView.vue') component: () => import('../views/OverviewQuizView.vue')
}, },
{ {
path: '/play-quiz', path: '/play-quiz/:quizId',
name: 'playQuiz', name: 'playQuiz',
component: () => import('../views/PlayQuizView.vue') component: () => import('../views/PlayQuizView.vue'),
params: true
},
{
path: '/edit-quiz/:quizId',
name: 'editQuiz',
component: EditQuizView,
params: true
}, },
] ]
}) })
......
import {apiClient} from "@/api.js";
export const getToken = () => { export const getToken = () => {
return localStorage.getItem('token'); return localStorage.getItem('token');
} }
...@@ -9,3 +11,9 @@ export const setToken = (token) => { ...@@ -9,3 +11,9 @@ export const setToken = (token) => {
export const removeToken = () => { export const removeToken = () => {
localStorage.removeItem('token'); localStorage.removeItem('token');
} }
export const getIdByToken= () => {
//const token = getToken();
//return apiClient.get('/user/getId');
return 'UserName';
}
\ No newline at end of file
<script setup> <script>
import NewQuestionModel from "@/components/shared/NewQuestionModel.vue"; import NewQuestionModel from "@/components/shared/NewQuestionModel.vue";
import {ref} from "vue"; import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import router from "@/router/index.js"; import router from "@/router/index.js";
import {apiClient} from "@/api.js"; import {apiClient} from "@/api.js";
import QuestionCard from "@/components/shared/QuestionCard.vue";
/**
const quizId = ref(null); //TODO: set quiz id when routing here const quizId = ref(null); //TODO: set quiz id when routing here
const createdQuestion = ref(null); const createdQuestion = ref(null);
const newAnswers = ref([]); const newAnswers = ref([]);
const showNewQuestionModal = ref(false); let showNewQuestionModal = ref(false);
const selectedAnswer = ref(null); const selectedAnswer = ref(null);
const existingQuestions = ref([]); const existingQuestions = ref([]);
let answerId = 1; let answerId = 1;
const errorMsg = ''; //TODO: display error to user const quizName = ref('');
const errorMsg = ''; //TODO: display error to user*/
export default {
components: {NewQuestionModel, QuestionCard},
data() {
return {
showNewQuestion: false,
quizId: null,
quizTitle: '',
questions: [
{id: 0, num: 1, text:'first question'},
{id: 1, num: 2, text:'second question'},
{id: 2, num: 3, text:'third question'}
],
category: '',
difficulty: '',
errorMsg: ''
//TODO: make quiz object
};
},
mounted() {
this.quizId = this.$route.params.quizId;
this.getQuiz(this.quizId);
},
methods: {
getQuiz(quizId) {
console.log('Fetching data for quiz: ', quizId);
/*
try {
apiClient.get('/quiz/quiz/' + this.quizId).then(response => {
this.quizTitle = JSON.parse(response.data.title);
this.category = JSON.parse(response.data.category);
this.difficulty = JSON.parse(response.data.difficulty);
this.questions = JSON.parse(JSON.stringify(response.data.questions));
});
} catch (error) {
//TODO: proper error handling
this.errorMsg = 'Error retrieving quizzes';
}*/
},
newQuestion() {
this.showNewQuestion = true;
console.log(this.showNewQuestion);
},
hideNewQuestion() {
this.showNewQuestion = false;
//TODO: update answers, +answer count
}
},
};
/**
function createQuestion() { function createQuestion() {
showNewQuestionModal.value = true; showNewQuestionModal.value = true;
} }
function destroyModal() { function destroyModal() {
showNewQuestionModal.value = false; showNewQuestionModal.value = false;
} }
function addNewAnswers() { function addNewAnswers() {
const newAnswer = { const newAnswer = {
id: answerId++, id: answerId++,
...@@ -38,8 +92,9 @@ function handleRadioToggle(Id) { ...@@ -38,8 +92,9 @@ function handleRadioToggle(Id) {
answer.correct_answer = 0 answer.correct_answer = 0
} }
}) })
} }*/
/**
function validateAnswers() { function validateAnswers() {
for(const answer of newAnswers.value) { for(const answer of newAnswers.value) {
if(answer.answer.trim()==='') { if(answer.answer.trim()==='') {
...@@ -55,7 +110,7 @@ function answerCount() { ...@@ -55,7 +110,7 @@ function answerCount() {
return true return true
} }
} }
/*
async function getExistingQuestions() { async function getExistingQuestions() {
try { try {
const response = await apiClient.get(`/quizzes/${quizId.value}/questions`); // Fetch questions for a specific quiz const response = await apiClient.get(`/quizzes/${quizId.value}/questions`); // Fetch questions for a specific quiz
...@@ -66,7 +121,9 @@ async function getExistingQuestions() { ...@@ -66,7 +121,9 @@ async function getExistingQuestions() {
alert('An error occurred while fetching existing questions'); alert('An error occurred while fetching existing questions');
} }
} }
*/
/**
async function submitQuestion() { async function submitQuestion() {
//TODO: proper error handling //TODO: proper error handling
if(!createdQuestion.value){ if(!createdQuestion.value){
...@@ -90,7 +147,7 @@ async function submitQuestion() { ...@@ -90,7 +147,7 @@ async function submitQuestion() {
this.errorMsg = 'Error logging in'; this.errorMsg = 'Error logging in';
} }
} }
*/
</script> </script>
...@@ -98,7 +155,14 @@ async function submitQuestion() { ...@@ -98,7 +155,14 @@ async function submitQuestion() {
<body> <body>
<div class="createQuestion-page"> <div class="createQuestion-page">
<router-link to="/overviewQuiz"> <- </router-link> <router-link to="/overviewQuiz"> <- </router-link>
<h1>Create a question to your quiz</h1> <h1>Edit quiz {{quizId}}</h1>
<p>Add questions</p>
<div class="question-div">
<QuestionCard question-id="questionCard" v-for="question in questions"
:key="question.id" :question-num="question.num" :question="question.text"/>
</div>
<NewQuestionModel v-if="showNewQuestion" @close="hideNewQuestion"/>
<!--
<div class="question-table"> <div class="question-table">
<table class="table"> <table class="table">
<thead> <thead>
...@@ -121,6 +185,9 @@ async function submitQuestion() { ...@@ -121,6 +185,9 @@ async function submitQuestion() {
</tbody> </tbody>
</table> </table>
-->
<!--
<Teleport to="body"> <Teleport to="body">
<NewQuestionModel :show="showNewQuestionModal" @close="destroyModal"> <NewQuestionModel :show="showNewQuestionModal" @close="destroyModal">
...@@ -166,11 +233,10 @@ async function submitQuestion() { ...@@ -166,11 +233,10 @@ async function submitQuestion() {
</NewQuestionModel> </NewQuestionModel>
</Teleport> </Teleport>
</div> </div>
-->
<div> <div>
<button @click="createQuestion" class="add-Btn"> Add Question </button> <br> <button @click="newQuestion" class="add-Btn"> Add Question </button> <br>
<button class="save-Btn"> SAVE QUIZ </button> <button class="save-Btn"> SAVE QUIZ </button>
</div> </div>
</div> </div>
......
<template> <template>
<body> <body>
<div class="overViewQuestion-page"> <div class="overViewQuestion-page">
<div class="headerDiv">
<div>
<router-link to="/dashboard"> <- </router-link> <router-link to="/dashboard"> <- </router-link>
<h1>Your quizzes</h1> <h1>Your quizzes</h1>
<p>Select a quiz for your creation to either play, edit or delete</p> <p>Select a quiz for your creation to either play, edit or delete</p>
<div class="row">
<div class="course-col">
<div class="quiz-header">
<h3>Quiz 1</h3>
<Svg :name="selectedIcon" />
</div> </div>
<div>
<div class="quiz-body"> <button class="add-Btn">Create Quiz</button>
<p>Info about quiz...</p>
<p>Category:</p>
<div id="form-box">
<form>
<select id="quiz-category-1" @change="changeCategory">
<option value="Animal">Animals</option>
<option value="Athletic">Athletic/Sport</option>
<option value="Computer" selected="selected">Computer Science</option>
<option value="Drama">Drama/Movie</option>
<option value="Music">Music</option>
<option value="Religion">Religion</option>
<option value="Science">Science</option>
<option value="Society">Society</option>
<option value="Other">Other</option>
</select>
</form>
</div> </div>
</div> </div>
<div class="quiz-footer">
<router-link to="/play-quiz" class="play-btn">Play</router-link>
<router-link to="/createQuiz" class="edit-btn">Edit</router-link>
<button class="delete-btn">Delete</button>
</div>
</div>
<div> <div class="row">
<button class="add-Btn">Create Quiz</button> <div class="quiz-div">
<QuizCard id="quizCard" v-for= "quiz in quizList" :key="quiz.id" :quizDescription="quiz.description" :quizName="quiz.name" :quiz-id="quiz.id" />
</div> </div>
</div> </div>
</div> </div>
</body> </body>
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import QuizCard from "@/components/shared/QuizCard.vue";
import Svg from "@/assets/Svg.vue"; import {getIdByToken} from "@/tokenController.js";
import {apiClient} from "@/api.js";
import {ref} from "vue";
getQuizzes();
const quizzes = ref([]);
async function getQuizzes() {
//TODO: try/catch
const response = await apiClient.get('/quizzes/${quizId.value}');
quizzes.value = response.data; //TODO: create parsing method
}
export default defineComponent({ export default {
components: { Svg }, components: {
QuizCard,
},
data() { data() {
return { return {
selectedCategory: 'Computer', userName: '',
categoryIcons: { quizNo: 0,
'Animal': 'animal-category', quizList: [
'Athletic': 'athletic-category', { name: 'Quiz 1', description: 'Description of Quiz 1', id: 1 },
'Computer': 'computer-category', { name: 'Quiz 2', description: 'Description of Quiz 2', id: 2 },
'Drama': 'drama-category', { name: 'Quiz 3', description: 'Description of Quiz 3', id: 3 },
'Music': 'music-category', { name: 'Quiz 4', description: 'Description of Quiz 4', id: 3 },
'Religion': 'religion-category', { name: 'Quiz 5', description: 'Description of Quiz 5', id: 3 }
'Science': 'science-category', ], //TODO: replace with request-method when ready, using quiz-objects
'Society': 'society-category',
'Other': 'other-category'
}
}; };
}, },
computed: { methods: {
selectedIcon() { /*
return this.categoryIcons[this.selectedCategory]; async populateQuizzes() {
try {
await apiClient.get('/quiz/creator/' + this.userId).then(response => {
this.quizList = JSON.parse(JSON.stringify(response.data));
//TODO: set max amt.
this.quizNo = this.quizList.length;
});
} catch (error) {
//TODO: proper error handling
this.errorMsg = 'Error retrieving quizzes';
} }
},*/
populateQuizzes() {
this.quizNo = this.quizList.length;
}, },
methods: { newQuiz() {
changeCategory(event) { //link to new quiz page
this.selectedCategory = event.target.value; },
setUserId() {
this.userName = getIdByToken();
}
},
created() {
this.setUserId();
this.populateQuizzes();
} }
} }
});
</script> </script>
<style> <style>
...@@ -100,26 +79,22 @@ export default defineComponent({ ...@@ -100,26 +79,22 @@ export default defineComponent({
padding: 50px; padding: 50px;
} }
.quiz-header{ .quiz-div {
display: flex; display: flex;
flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
align-items: center; float: left;
margin-bottom: 20px; box-sizing: border-box;
}
.quiz-body{
margin-bottom: 40px;
} }
#quiz-category{
padding: 1px; .headerDiv {
background-color: #FFF;
border-radius: 5px;
font-family: monospace;
}
.quiz-footer{
display: flex; display: flex;
justify-content: space-evenly; width: 100%;
align-items: center; justify-content: space-between;
padding-left: 25px; }
padding-right: 25px;
#quizCard {
width: calc(50% - 10px);
} }
</style> </style>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment