diff --git a/FullstackProsjekt/src/frontend/src/App.vue b/FullstackProsjekt/src/frontend/src/App.vue index 25bbd9a9bf6562e6bdb84c7ea954b1cd1e0ef95c..d1c19425340a2fcbbb9e78afb8c6ce158f7dae48 100644 --- a/FullstackProsjekt/src/frontend/src/App.vue +++ b/FullstackProsjekt/src/frontend/src/App.vue @@ -1,7 +1,7 @@ <script> import { RouterLink, RouterView } from 'vue-router' -import Sidebar from "@/components/sidebar/Sidebar.vue" -import { sidebarWidth} from "@/components/sidebar/state.js"; +import Sidebar from "@/components/shared/sidebar/Sidebar.vue" +import { sidebarWidth} from "@/components/shared/sidebar/state.js"; export default { components:{Sidebar}, setup(){ diff --git a/FullstackProsjekt/src/frontend/src/assets/icon.css b/FullstackProsjekt/src/frontend/src/assets/icon.css new file mode 100644 index 0000000000000000000000000000000000000000..629beb9e39ce336c748b8a7bc4960f9630961b68 --- /dev/null +++ b/FullstackProsjekt/src/frontend/src/assets/icon.css @@ -0,0 +1,4 @@ +.icon-home{ + background-image: url("../components/icons/home.png"); + height: 50px; +} \ No newline at end of file diff --git a/FullstackProsjekt/src/frontend/src/assets/main.css b/FullstackProsjekt/src/frontend/src/assets/main.css index 0d449044eb0385b0e2cdbd2d1b8dd663120c93cc..18ef9a814c3d3e95bb716565f87f83a379c66594 100644 --- a/FullstackProsjekt/src/frontend/src/assets/main.css +++ b/FullstackProsjekt/src/frontend/src/assets/main.css @@ -1,4 +1,80 @@ body { - font-family: Arial, sans-serif; + font-family: monospace; margin: 0 0 0 27px; + background-color: #F2F2F2; } +.table{ + width: 450px; + padding: 10px; + background-color: #dcdcdc; +} + +.play-btn{ + height: 25px; + padding: 5px; + text-decoration: none; + background-color: #242F40; + border-color: transparent; + color: #FFFFFF; +} +.edit-btn{ + height: 25px; + padding: 5px; + text-decoration: none; + background-color: #CCA43B; + border-color: transparent; + color: #242F40; +} +.delete-btn{ + height: 25px; + padding: 5px; + text-decoration: none; + background-color: #cc513b; + border-color: transparent; + color: #242F40; +} +.add-Btn { + background-color: #242F40; + color: white; + font-size: 16px; + padding: 10px; + border-radius: 20px; + border-color: transparent; + margin-top: 10px; +} +.save-Btn{ + background-color: #CCA43B; + font-size: 24px; + padding: 15px; + border-radius: 20px; + border-color: transparent; + margin-top: 10px; +} + +.close-btn{ + background-color: #cc513b; + padding: 10px; + border-radius: 5px; + border-color: transparent; + margin-left: 10px; + +} + +.submit-btn{ + background-color: #CCA43B; + font-size: 16px; + padding: 10px; + border-radius: 5px; + border-color: transparent; + margin-left: 130px; +} + + + + + + + + + + diff --git a/FullstackProsjekt/src/frontend/src/components/TheWelcome.vue b/FullstackProsjekt/src/frontend/src/components/TheWelcome.vue index 62364610e13ee8529489465f875d8c146ac605af..2c9155318503a699a2664335844a3f3da79f863a 100644 --- a/FullstackProsjekt/src/frontend/src/components/TheWelcome.vue +++ b/FullstackProsjekt/src/frontend/src/components/TheWelcome.vue @@ -18,7 +18,7 @@ export default { <img id="logo" src="../components/icons/brain.png"/> <h1 class="heading">BrainStormer</h1> <p> An easy way to learn and share quizzes. <br> Make your own quiz now! </p> - <a href="" class="hero-btn">CREATE QUIZ</a> + <router-link to="/overviewQuiz" class="hero-btn">CREATE QUIZ </router-link> </div> </section> @@ -98,6 +98,7 @@ export default { border: 1px solid #CCA43B; background: #CCA43B; transition: 1s; + color: #242F40; } /*Info with three levels*/ @@ -125,7 +126,7 @@ p{ } .course-col{ flex-basis: 31%; - background: #E5E5E5; + background: #cccaca; border-radius: 10px; margin-bottom: 5%; padding: 20px; @@ -139,6 +140,7 @@ h3{ } .course-col:hover{ box-shadow: 0 0 20px 0px rgba(0,0,0,0.3); + } @media (max-width: 700px){ diff --git a/FullstackProsjekt/src/frontend/src/components/shared/NewQuestionModel.vue b/FullstackProsjekt/src/frontend/src/components/shared/NewQuestionModel.vue new file mode 100644 index 0000000000000000000000000000000000000000..f04f8fdf7ea5f415b19a6f132c1bdfe16af18243 --- /dev/null +++ b/FullstackProsjekt/src/frontend/src/components/shared/NewQuestionModel.vue @@ -0,0 +1,75 @@ +<script setup> +const props = defineProps({ + show: Boolean +}) +</script> + +<template> + <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; +} + +.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 diff --git a/FullstackProsjekt/src/frontend/src/components/sidebar/Sidebar.vue b/FullstackProsjekt/src/frontend/src/components/shared/sidebar/Sidebar.vue similarity index 88% rename from FullstackProsjekt/src/frontend/src/components/sidebar/Sidebar.vue rename to FullstackProsjekt/src/frontend/src/components/shared/sidebar/Sidebar.vue index 4952a22e17b573eb1e330347b49342afa03e485b..a6a6af34ac1785546260d93e85c5dcd0f420f556 100644 --- a/FullstackProsjekt/src/frontend/src/components/sidebar/Sidebar.vue +++ b/FullstackProsjekt/src/frontend/src/components/shared/sidebar/Sidebar.vue @@ -1,6 +1,6 @@ <script> -import {collapsed, toggleSideBar, sidebarWidth} from "@/components/sidebar/state.js"; -import SidebarLink from "@/components/sidebar/SidebarLink.vue"; +import {collapsed, toggleSideBar, sidebarWidth} from "@/components/shared/sidebar/state.js"; +import SidebarLink from "@/components/shared/sidebar/SidebarLink.vue"; export default { components: {SidebarLink}, @@ -30,7 +30,7 @@ export default { <SidebarLink to="/login" icon="fas fa-image">Login</SidebarLink> <span class="collapse-icon" :class="{'rotate-180': collapsed}" @click="toggleSideBar"> - <button class="iconButton"> <img id="icon" src="../icons/sidebar-arrow.png"/> </button> + <button class="iconButton"> <img id="icon" src="../../icons/sidebar-arrow.png"/> </button> </span> </div> </template> diff --git a/FullstackProsjekt/src/frontend/src/components/sidebar/SidebarLink.vue b/FullstackProsjekt/src/frontend/src/components/shared/sidebar/SidebarLink.vue similarity index 95% rename from FullstackProsjekt/src/frontend/src/components/sidebar/SidebarLink.vue rename to FullstackProsjekt/src/frontend/src/components/shared/sidebar/SidebarLink.vue index 69bb7ad1c07a91eba669c3c5b95f387154ab86cd..8555eca13ae60bbc4664a2500c76d106d885e359 100644 --- a/FullstackProsjekt/src/frontend/src/components/sidebar/SidebarLink.vue +++ b/FullstackProsjekt/src/frontend/src/components/shared/sidebar/SidebarLink.vue @@ -1,7 +1,7 @@ <script> import { computed } from 'vue' import { useRoute } from 'vue-router' -import { collapsed } from './state' +import { collapsed } from './state.js' export default { props: { @@ -62,6 +62,7 @@ export default { .link.active { background-color: var(--sidebar-item-active); + color: #242F40; } .link .icon { diff --git a/FullstackProsjekt/src/frontend/src/components/sidebar/state.js b/FullstackProsjekt/src/frontend/src/components/shared/sidebar/state.js similarity index 100% rename from FullstackProsjekt/src/frontend/src/components/sidebar/state.js rename to FullstackProsjekt/src/frontend/src/components/shared/sidebar/state.js diff --git a/FullstackProsjekt/src/frontend/src/router/index.js b/FullstackProsjekt/src/frontend/src/router/index.js index 8e5fec7623ee412f1780bfe57fe74e8cc1ff8c28..41c86ae42b3abf3c25d4fefca20894319c4dc97e 100644 --- a/FullstackProsjekt/src/frontend/src/router/index.js +++ b/FullstackProsjekt/src/frontend/src/router/index.js @@ -34,6 +34,21 @@ const router = createRouter({ name: 'signup', component: () => import('../views/SignupView.vue') }, + { + path: '/createQuiz', + name: 'create Quiz', + component: () => import('../views/EditQuizView.vue') + }, + { + path: '/overviewQuiz', + name: 'overview Quiz', + component: () => import('../views/OverviewQuizView.vue') + }, + { + path: '/play-quiz', + name: 'playQuiz', + component: () => import('../views/PlayQuizView.vue') + }, ] }) diff --git a/FullstackProsjekt/src/frontend/src/views/DashboardView.vue b/FullstackProsjekt/src/frontend/src/views/DashboardView.vue index a446e69ccc6e95c13b82841f3359a626e1d91b87..e62d15ee4f71331307cac86dbdad81f52b3ff268 100644 --- a/FullstackProsjekt/src/frontend/src/views/DashboardView.vue +++ b/FullstackProsjekt/src/frontend/src/views/DashboardView.vue @@ -4,11 +4,12 @@ <template> <body class="dashboard"> <h1>Dashboard</h1> - <input class="searchBox" placeholder="Search"> - + <input class="searchBox" placeholder="Search for category..."> <br> + <router-link to="/overviewQuiz" class="hero-btn">CREATE QUIZ </router-link> <div class="row"> <div class="course-col"> <h3>Quiz 1</h3> + <img id="quizImg" src="../components/photos/background.png"/> <p>Insert photo </p> </div> <div class="course-col"> @@ -42,10 +43,29 @@ <style> .dashboard{ - padding: 10px; + padding: 20px; } .searchBox{ + width: 250px; padding: 10px; + margin-bottom: 50px; +} + +.hero-btn{ + display: inline-block; + text-decoration: none; + color: #242F40; + border: 1px solid #242F40; + padding: 12px 34px; + font-size: 16px; + background: transparent; + position: relative; + cursor: pointer; +} +.hero-btn:hover{ + border: 1px solid #CCA43B; + background: #CCA43B; + transition: 1s; } .row{ @@ -65,4 +85,22 @@ .course-col:hover{ box-shadow: 0 0 20px 0px rgba(0,0,0,0.3); } + +#quizImg{ + height: 200px; + border-radius: 5px; + display: flex; + +} + +@media (max-width: 700px){ + .text-box h1{ + font-size: 42px; + transition: 1s; + } + .row{ + flex-direction: column; + } + +} </style> \ No newline at end of file diff --git a/FullstackProsjekt/src/frontend/src/views/EditQuizView.vue b/FullstackProsjekt/src/frontend/src/views/EditQuizView.vue new file mode 100644 index 0000000000000000000000000000000000000000..064b4fd76a05ff7d631d9d043493b0eb710831f6 --- /dev/null +++ b/FullstackProsjekt/src/frontend/src/views/EditQuizView.vue @@ -0,0 +1,167 @@ +<script setup> +import NewQuestionModel from "@/components/shared/NewQuestionModel.vue"; +import {ref} from "vue"; +import router from "@/router/index.js"; + +const createdQuestion = ref(null); +const newAnswers = ref([]); +const showNewQuestionModal = ref(false); +const selectedAnswer = ref(null); +let answerId = 1; + + +function createQuestion() { + showNewQuestionModal.value = true; +} +function destroyModal() { + showNewQuestionModal.value = false; +} +function addNewAnswers() { + const newAnswer = { + id: answerId++, + answer: '', + correct_answer: 0 + } + newAnswers.value.push(newAnswer) +} +function handleRadioToggle(Id) { + selectedAnswer.value = Id + newAnswers.value.forEach(answer => { + if(answer.id === Id) { + answer.correct_answer = 1 + } else { + answer.correct_answer = 0 + } + }) +} + +function validateAnswers() { + for(const answer of newAnswers.value) { + if(answer.answer.trim()==='') { + return false + } + } + return true +} +function answerCount() { + if(newAnswers.length < 2) { + alert('Two answers required to submit') + } else if(newAnswers.length === 2) { + return true + } +} + +function submitQuestion() { + if(!createdQuestion.value){ + alert('Question cannot be empty'); + return false + } + if(!validateAnswers() && !answerCount()){ + alert('Fill all inputs before submitting') + return false + } + //Supposed to send request to backend with a finnished Question with id and such + router.post('/questions',{ + question:createdQuestion.value, + answers:newAnswers.value + }) +} + +</script> + + +<template> + <body> + <div class="createQuestion-page"> + <router-link to="/overviewQuiz"> <- </router-link> + <h1>Create a question to your quiz</h1> + <div class="question-table"> + <table class="table"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Question</th> + <th scope="col">Action</th> + </tr> + </thead> + <tbody> + <tr> + <th scope="row">1</th> + <td>What is Vue?</td> + <td> + <button class="play-btn">View</button> + <button class="edit-btn">Edit</button> + <button class="delete-btn"> Delete</button> + </td> + </tr> + + </tbody> + </table> + + <Teleport to="body"> + <NewQuestionModel :show="showNewQuestionModal" @close="destroyModal"> + + <template #header> + <h5> Add New Question</h5> + </template> + + <template #body> + <form> + <div class="mb-3"> + <label for="question" class="form-label">Question</label> <br> + <input type="text" v-model="createdQuestion" class="form-control" id="questionInput"> + </div> + <table class="table"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Answer</th> + <th scope="col">Correct ?</th> + </tr> + </thead> + <tbody> + <tr v-for="(answer, index) in newAnswers"> + <th scope="row">{{answer.id}}</th> + <td> + <input type="text" v-model="answer.answer" id="questionInput"> + </td> + <td> + <input :checked="answer.correct_answer === 1" class="form-check-input" :value="answer.id" @change="handleRadioToggle(answer.id)" type="radio"> + </td> + </tr> + </tbody> + </table> + </form> + </template> + + <template #footer> + <button @click="addNewAnswers" class="add-Btn" v-if="newAnswers.length<4" >+</button> + <button @click="destroyModal" class="close-btn"> Close</button> + <button v-if="newAnswers.length>=2" @click="submitQuestion" class="submit-btn">Submit</button> + </template> + + </NewQuestionModel> + </Teleport> + + </div> + + <div> + <button @click="createQuestion" class="add-Btn"> Add Question </button> <br> + <button class="save-Btn"> SAVE QUIZ </button> + </div> + </div> + </body> + +</template> + +<style> +.createQuestion-page{ + padding: 20px; +} + +input{ + height: 25px; + width: 100%; +} + +</style> \ No newline at end of file diff --git a/FullstackProsjekt/src/frontend/src/views/OverviewQuizView.vue b/FullstackProsjekt/src/frontend/src/views/OverviewQuizView.vue new file mode 100644 index 0000000000000000000000000000000000000000..25c153ea087db0c9b81d01b37a2f67ca82e8ba48 --- /dev/null +++ b/FullstackProsjekt/src/frontend/src/views/OverviewQuizView.vue @@ -0,0 +1,80 @@ +<script> + +</script> + + +<template> + <body> + <div class="overViewQuestion-page"> + <router-link to="/dashboard"> <- </router-link> + <h1>Your quizzes</h1> + <p> Select a quiz fo your creation to either play, edit or delete</p> + <div class="table"> + <table class="table"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Quiz</th> + <th scope="col">Action</th> + <th scope="col">Category </th> + </tr> + </thead> + <tbody> + <tr> + <th scope="row">1</th> + <td>Vue Quiz</td> + <td> + <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> + </td> + <th> + <div id="form-box"> + <form> + <select id="language"> + <option value="IT" selected="selected">IT</option> + <option value="Nature">Nature</option> + <option value="Chemistry">Chemistry</option> + <option value="Religion">Religion</option> + <option value="Other">Other</option> + </select> + </form> + </div> + </th> + </tr> + <tr> + <th scope="row">2</th> + <td>What is Java?</td> + <td> + <router-link to="/play-quiz" class="play-btn">Play</router-link> + <router-link to="/overviewQuiz" class="edit-btn">Edit</router-link> + <button class="delete-btn"> Delete</button> + </td> + <th> + <div id="form-box"> + <form> + <select id="language"> + <option value="IT" selected="selected">IT</option> + <option value="Nature">Nature</option> + <option value="Chemistry">Chemistry</option> + <option value="Religion">Religion</option> + <option value="Other">Other</option> + </select> + </form> + </div> + </th> + </tr> + </tbody> + </table> + </div> + </div> + + </body> + +</template> + +<style> +.overViewQuestion-page{ + padding: 20px; +} +</style> \ No newline at end of file diff --git a/FullstackProsjekt/src/frontend/src/views/PlayQuizView.vue b/FullstackProsjekt/src/frontend/src/views/PlayQuizView.vue new file mode 100644 index 0000000000000000000000000000000000000000..2ec7c476d8a358f9aff4ffe6da537206d7cd02bf --- /dev/null +++ b/FullstackProsjekt/src/frontend/src/views/PlayQuizView.vue @@ -0,0 +1,205 @@ +<script > +</script> + + +<template> + + <body> + + <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>Question</h1> + </div> + + <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> + + <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> + + </body> + +</template> + +<style> +.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; +} + +/*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>