Added a lot of routes and views
This commit is contained in:
parent
152a199ee2
commit
7d4d6b354c
@ -1,11 +1,20 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { mdiAccountGroupOutline, mdiAccountQuestion, mdiCodeTags, mdiHandCoin, mdiHome, mdiInformation } from '@mdi/js'
|
import { mdiAccountBoxMultiple, mdiAccountGroupOutline, mdiAccountQuestion, mdiChartBox, mdiCodeTags, mdiCounter, mdiEarth, mdiHandCoin, mdiHome, mdiInformation, mdiWeb } from '@mdi/js'
|
||||||
import About from '@/views/about/Index.vue'
|
import About from '@/views/about/Index.vue'
|
||||||
import AboutPage from '@/views/about/About.vue'
|
import AboutPage from '@/views/about/About.vue'
|
||||||
import Acknowledgements from '@/views/about/Acknowledgements.vue'
|
import Acknowledgements from '@/views/about/Acknowledgements.vue'
|
||||||
import Wanderhome from '@/views/about/Wanderhome.vue'
|
import Wanderhome from '@/views/about/Wanderhome.vue'
|
||||||
import Home from '@/views/Home.vue'
|
import Home from '@/views/Home.vue'
|
||||||
import Quiz from '@/views/Quiz.vue'
|
import Quiz from '@/views/quiz/Index.vue'
|
||||||
|
import QuizPage from '@/views/quiz/Quiz.vue'
|
||||||
|
import Question from '@/views/quiz/Question.vue'
|
||||||
|
import Error404 from '@/views/errors/Error404.vue'
|
||||||
|
import Refused from '@/views/errors/Refused.vue'
|
||||||
|
import Results from '@/views/results/Index.vue'
|
||||||
|
import ResultsPage from '@/views/results/Results.vue'
|
||||||
|
import Scores from '@/views/results/Scores.vue'
|
||||||
|
import Answers from '@/views/results/Answers.vue'
|
||||||
|
import Statistics from '@/views/results/Statistics.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -26,8 +35,33 @@ const routes = [
|
|||||||
index: 1,
|
index: 1,
|
||||||
svgPath: mdiAccountQuestion,
|
svgPath: mdiAccountQuestion,
|
||||||
title: 'Take the Quiz'
|
title: 'Take the Quiz'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'Quiz',
|
||||||
|
component: QuizPage,
|
||||||
|
meta: {
|
||||||
|
index: 0,
|
||||||
|
parentIndex: 1,
|
||||||
|
title: 'Take the Quiz'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'question/:id',
|
||||||
|
name: 'Question',
|
||||||
|
component: Question,
|
||||||
|
props: route => ({
|
||||||
|
id: parseInt(route.params.id)
|
||||||
|
}),
|
||||||
|
meta: {
|
||||||
|
index: 1,
|
||||||
|
parentIndex: 1,
|
||||||
|
title: 'Take the Quiz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
name: 'About',
|
name: 'About',
|
||||||
@ -82,6 +116,92 @@ const routes = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/results',
|
||||||
|
name: 'Results',
|
||||||
|
component: Results,
|
||||||
|
meta: {
|
||||||
|
index: -1,
|
||||||
|
title: 'Results',
|
||||||
|
svgPath: mdiAccountBoxMultiple
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'Result',
|
||||||
|
component: ResultsPage,
|
||||||
|
meta: {
|
||||||
|
index: 0,
|
||||||
|
title: 'Your Results',
|
||||||
|
svgPath: mdiAccountBoxMultiple,
|
||||||
|
parentIndex: -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'all',
|
||||||
|
name: 'Scores',
|
||||||
|
component: Scores,
|
||||||
|
meta: {
|
||||||
|
index: 1,
|
||||||
|
title: 'All Scores',
|
||||||
|
svgPath: mdiCounter,
|
||||||
|
parentIndex: -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'statistics',
|
||||||
|
name: 'Statistics',
|
||||||
|
component: Statistics,
|
||||||
|
meta: {
|
||||||
|
index: 2,
|
||||||
|
title: 'Statistics',
|
||||||
|
svgPath: mdiChartBox,
|
||||||
|
parentIndex: -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'answers',
|
||||||
|
name: 'Answers',
|
||||||
|
component: Answers,
|
||||||
|
meta: {
|
||||||
|
index: 3,
|
||||||
|
title: 'Global Answers',
|
||||||
|
svgPath: mdiEarth,
|
||||||
|
parentIndex: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
name: 'Error404',
|
||||||
|
component: Error404,
|
||||||
|
meta: {
|
||||||
|
index: -1,
|
||||||
|
title: 'Error 404: Not Found',
|
||||||
|
svgPath: mdiWeb
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/:catchAll(.*)",
|
||||||
|
name: 'NotFound',
|
||||||
|
redirect: '/404',
|
||||||
|
meta: {
|
||||||
|
index: -1,
|
||||||
|
title: 'Error 404: Not Found',
|
||||||
|
svgPath: mdiWeb
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/err_refused",
|
||||||
|
name: 'Refused',
|
||||||
|
component: Refused,
|
||||||
|
meta: {
|
||||||
|
index: -1,
|
||||||
|
title: 'Error: Connection Refused',
|
||||||
|
svgPath: mdiWeb
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -100,8 +220,8 @@ Router.beforeEach((to, from, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Router.afterEach( (to, from) => {
|
Router.afterEach( (to, from) => {
|
||||||
const enterActiveClass = 'transition-all duration-300 ease-in'
|
const enterActiveClass = 'transition-all duration-300 origin-top ease-in'
|
||||||
const leaveActiveClass = 'transition-all duration-300 ease-out'
|
const leaveActiveClass = 'transition-all duration-300 origin-top ease-out'
|
||||||
const baseFromClass = 'transform opacity-0 md:scale-75'
|
const baseFromClass = 'transform opacity-0 md:scale-75'
|
||||||
if ( to.fullPath.split('/')[1] !== from.fullPath.split('/')[1] || !from.matched.length ) {
|
if ( to.fullPath.split('/')[1] !== from.fullPath.split('/')[1] || !from.matched.length ) {
|
||||||
const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.meta.index
|
const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.meta.index
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
<script>
|
<script>
|
||||||
import Content from '@/components/Content.vue'
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
import AccountQuestion from '@/components/icons/AccountQuestion.vue'
|
import AccountQuestion from '@/components/icons/AccountQuestion.vue'
|
||||||
export default {
|
export default {
|
||||||
|
name: 'Home',
|
||||||
components: {
|
components: {
|
||||||
AccountQuestion,
|
AccountQuestion,
|
||||||
Content
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Content>
|
<TextFrame>
|
||||||
<template v-slot:header>
|
<Header>
|
||||||
home
|
home
|
||||||
</template>
|
</Header>
|
||||||
<template v-slot:content>
|
<Content>
|
||||||
<article class="prose mx-auto">
|
<article class="prose mx-auto">
|
||||||
<p class="text-leader">
|
<p class="text-leader">
|
||||||
The road ahead is long and meandering.
|
The road ahead is long and meandering.
|
||||||
@ -28,11 +33,11 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="w-fit mx-auto">
|
<div class="w-fit mx-auto">
|
||||||
<router-link active-class="active-link" class="navlink" to="/quiz">
|
<router-link active-class="active-link" class="navlink" to="/quiz">
|
||||||
<div class="inline-flex px-3 py-1 space-x-2 rounded-md bg-lime-800 text-white hover:bg-lime-600">
|
<div class="inline-flex px-3 py-1 space-x-2 rounded-md bg-lime-800 text-white transition-all duration-300 ease-in-out hover:bg-lime-600">
|
||||||
<span class="scale-75">
|
<span class="scale-75">
|
||||||
<AccountQuestion/>
|
<AccountQuestion/>
|
||||||
</span>
|
</span>
|
||||||
<span>Take the Quiz</span>
|
<span class="uncial-antiqua">Take the Quiz</span>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
@ -42,6 +47,6 @@
|
|||||||
I would nevertheless implore you to play <em>Wanderhome</em> if you haven’t already.
|
I would nevertheless implore you to play <em>Wanderhome</em> if you haven’t already.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
|
||||||
</Content>
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
</template>
|
</template>
|
@ -1,18 +1,22 @@
|
|||||||
<script>
|
<script>
|
||||||
import Content from '@/components/Content.vue'
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
export default {
|
export default {
|
||||||
|
name: 'About',
|
||||||
components: {
|
components: {
|
||||||
Content
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div >
|
<TextFrame>
|
||||||
<Content>
|
<Header>
|
||||||
<template v-slot:header>
|
|
||||||
Background
|
Background
|
||||||
</template>
|
</Header>
|
||||||
<template v-slot:content>
|
<Content>
|
||||||
<article class="prose mx-auto">
|
<article class="prose mx-auto">
|
||||||
<h2>What Exactly Is This?</h2>
|
<h2>What Exactly Is This?</h2>
|
||||||
<p>
|
<p>
|
||||||
@ -87,9 +91,8 @@
|
|||||||
It will apply all the things I currently know, and also add websockets and state synchronisation to the mix.
|
It will apply all the things I currently know, and also add websockets and state synchronisation to the mix.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
|
||||||
</Content>
|
</Content>
|
||||||
</div>
|
</TextFrame>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
</style>
|
</style>
|
@ -1,18 +1,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import Content from '@/components/Content.vue'
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
export default {
|
export default {
|
||||||
|
name: 'Acknowledgements',
|
||||||
components: {
|
components: {
|
||||||
Content
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Content>
|
<TextFrame>
|
||||||
<template v-slot:header>
|
<Header>
|
||||||
Acknowledgements
|
Acknowledgements
|
||||||
</template>
|
</Header>
|
||||||
<template v-slot:content>
|
<Content>
|
||||||
<article class="prose mx-auto">
|
<article class="prose mx-auto">
|
||||||
<p>
|
<p>
|
||||||
First and foremost, I am delighted that Jay Dragon and Possum Creek Games created such a magnificent work of art.
|
First and foremost, I am delighted that Jay Dragon and Possum Creek Games created such a magnificent work of art.
|
||||||
@ -27,7 +32,7 @@
|
|||||||
This section is still a work in progress because the programme is still not yet finished.
|
This section is still a work in progress because the programme is still not yet finished.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
|
||||||
</Content>
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
@ -1,9 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import Content from '@/components/Content.vue'
|
|
||||||
import Sidebar from '@/components/sidebar/Index.vue'
|
import Sidebar from '@/components/sidebar/Index.vue'
|
||||||
export default {
|
export default {
|
||||||
|
name: 'AboutIndex',
|
||||||
components: {
|
components: {
|
||||||
Content,
|
|
||||||
Sidebar
|
Sidebar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import Content from '@/components/Content.vue'
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
export default {
|
export default {
|
||||||
|
name: 'Wanderhome',
|
||||||
components: {
|
components: {
|
||||||
Content
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Content>
|
<TextFrame>
|
||||||
<template v-slot:header>
|
<Header>
|
||||||
About <em>Wanderhome</em>
|
About <em>Wanderhome</em>
|
||||||
</template>
|
</Header>
|
||||||
<template v-slot:content>
|
<Content>
|
||||||
<article class="prose mx-auto">
|
<article class="prose mx-auto">
|
||||||
<p>
|
<p>
|
||||||
<em class="uncial-antiqua">Wanderhome</em> is a pastoral fantasy table-top role-playing game by Jay Dragon, published by Possum Creek Games.
|
<em class="uncial-antiqua">Wanderhome</em> is a pastoral fantasy table-top role-playing game by Jay Dragon, published by Possum Creek Games.
|
||||||
@ -39,7 +44,7 @@
|
|||||||
If you are one of my friends and would like to play <em class="uncial-antiqua">Wanderhome</em>, let me know!
|
If you are one of my friends and would like to play <em class="uncial-antiqua">Wanderhome</em>, let me know!
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
|
||||||
</Content>
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
38
client/src/views/errors/Error404.vue
Normal file
38
client/src/views/errors/Error404.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
export default {
|
||||||
|
name: 'Error404',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div >
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
404: Not Found
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<article class="prose mx-auto">
|
||||||
|
<p class="text-leader">
|
||||||
|
Uh-oh.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Sometimes we can get lost along the path.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
But it’s okay.
|
||||||
|
Just retrace your steps and you will find your way back again.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
34
client/src/views/errors/Refused.vue
Normal file
34
client/src/views/errors/Refused.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
export default {
|
||||||
|
name: 'Refused',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div >
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Error: Connection Refused
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<article class="prose mx-auto">
|
||||||
|
<p class="text-leader">
|
||||||
|
The client is unable to connect to the server.
|
||||||
|
That’s okay.
|
||||||
|
Sometimes, we run into obstacles along the way.
|
||||||
|
And we just need to try again after a while.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
63
client/src/views/quiz/Index.vue
Normal file
63
client/src/views/quiz/Index.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<script>
|
||||||
|
import axios from 'axios'
|
||||||
|
import Config from '@/config.js'
|
||||||
|
import { useQuestionStore } from '@/stores/questions.js'
|
||||||
|
import { useAnswersStore } from '@/stores/answers.js'
|
||||||
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const questionStore = useQuestionStore()
|
||||||
|
const answersStore = useAnswersStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
return {
|
||||||
|
questionStore,
|
||||||
|
answersStore,
|
||||||
|
appStore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'QuestionIndex',
|
||||||
|
created() {
|
||||||
|
if (!this.appStore.hasData) {
|
||||||
|
this.getQuestions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
error: null,
|
||||||
|
loading: false,
|
||||||
|
questions: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getQuestions() {
|
||||||
|
this.error = this.questions = null
|
||||||
|
this.loading = true
|
||||||
|
axios.get(`${Config.SERVER}api/questions/`)
|
||||||
|
.then((response) => {
|
||||||
|
this.error = false
|
||||||
|
this.questionStore.storeQuestions(response.data)
|
||||||
|
this.appStore.toggleData()
|
||||||
|
})
|
||||||
|
.catch( error => {
|
||||||
|
this.$router.push('/err_refused')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view v-slot="{ Component, route }" class="w-full" ref="panel" appear>
|
||||||
|
<Transition mode="out-in"
|
||||||
|
enter-active-class="transition-all duration-300 origin-center ease-in"
|
||||||
|
enter-from-class="transform opacity-0 md:scale-95"
|
||||||
|
leave-active-class="transition-all duration-300 origin-center ease-out"
|
||||||
|
leave-to-class="transform opacity-0 md:scale-95"
|
||||||
|
>
|
||||||
|
<component :is="Component" :key="route.path"/>
|
||||||
|
</Transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
219
client/src/views/quiz/Question.vue
Normal file
219
client/src/views/quiz/Question.vue
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<script>
|
||||||
|
import axios from 'axios'
|
||||||
|
import Config from '@/config.js'
|
||||||
|
import { useQuestionStore } from '@/stores/questions.js'
|
||||||
|
import { useAnswersStore } from '@/stores/answers.js'
|
||||||
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
import ArrowLeftBold from '@/components/icons/ArrowLeftBold.vue'
|
||||||
|
import ArrowRightBold from '@/components/icons/ArrowRightBold.vue'
|
||||||
|
import FleurDeLis from '@/components/icons/FleurDeLis.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const questionStore = useQuestionStore()
|
||||||
|
const answersStore = useAnswersStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
return {
|
||||||
|
questionStore,
|
||||||
|
answersStore,
|
||||||
|
appStore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ArrowLeftBold,
|
||||||
|
ArrowRightBold,
|
||||||
|
Content,
|
||||||
|
FleurDeLis,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.answersStore.makeArray(this.id, this.questionStore.questions[this.id].select)
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$watch(
|
||||||
|
() => this.$route.params, (toParams, previousParams) => {
|
||||||
|
if (this.$route.name == 'Question') {
|
||||||
|
if (toParams.id >= this.questionStore.questions.length || this.id < 0) {
|
||||||
|
this.$router.push('/404')
|
||||||
|
} else {
|
||||||
|
if ( toParams.id == 0 ) {
|
||||||
|
this.enableBack = false
|
||||||
|
}
|
||||||
|
if ( toParams.id == this.questionStore.questions.length - 1 ) {
|
||||||
|
console.log('Condition met')
|
||||||
|
this.enableNext = false
|
||||||
|
} else {
|
||||||
|
this.enableNext = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.$watch(
|
||||||
|
() => this.appStore.hasData, (newValue) => {
|
||||||
|
if (this.id >= this.questionStore.questions.length || this.id < 0) {
|
||||||
|
this.$router.push('/404')
|
||||||
|
} else {
|
||||||
|
this.answersStore.makeArray(this.id, this.questionStore.questions[this.id].select)
|
||||||
|
if ( this.id == this.questionStore.questions.length -1 ) {
|
||||||
|
this.enableNext = false
|
||||||
|
} else {
|
||||||
|
this.enableNext = true
|
||||||
|
}
|
||||||
|
if ( this.id == 0 ) {
|
||||||
|
this.enableBack = false
|
||||||
|
} else {
|
||||||
|
this.enableBack = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
question: null,
|
||||||
|
select: null,
|
||||||
|
answers: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitAnswers() {
|
||||||
|
const data = JSON.stringify(this.answersStore.answers)
|
||||||
|
axios.post(
|
||||||
|
`${Config.SERVER}api/submit/`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then( (response) => {
|
||||||
|
this.appStore.storeResults(response.data)
|
||||||
|
console.log('Results fetched from the server.')
|
||||||
|
this.$router.push('/results')
|
||||||
|
}).catch( (error) => {
|
||||||
|
this.$router.push('/err_refused')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Take the Quiz
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<div v-if="questionStore.questions[this.id]">
|
||||||
|
<article class="prose">
|
||||||
|
<div v-html="questionStore.questions[this.id].question"></div>
|
||||||
|
</article>
|
||||||
|
<div class="grid grid-cols-1 content-evenly items-center gap-2 justify-center my-12 mx-auto md:grid-cols-2 lg:grid-cols-3" :aria-multiselectable="questionStore.questions[this.id].select > 1 ? true: false">
|
||||||
|
<div v-for="(answer, index) in questionStore.questions[this.id].answers" :key="`q${this.id}-o${index}`" class="inline-flex">
|
||||||
|
<input :type="this.questionStore.questions[this.id].select > 1 ? 'checkbox' : 'radio'" class="checkbox" :id="`q${this.id}-o${index}`" :value=index :key="`q${this.id}-o${index}`" v-model="answersStore.answers[this.id]" :disabled="this.questionStore.questions[this.id].select > 1 && this.answersStore.answers[this.id].length == this.questionStore.questions[this.id].select && !this.answersStore.answers[this.id].includes(index)">
|
||||||
|
<label :for="`q${this.id}-o${index}`">
|
||||||
|
<div v-html="answer" class="uncial-antiqua w-full h-full p-2 transition-all duration-100 ease-in-out hover:bg-lime-200 hover:text-orange-600 hover:cursor-pointer" :aria-label="answer" :title="answer"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex mx-auto items-center justify-between max-w-sm">
|
||||||
|
<router-link :to="`/quiz/question/${this.id-1}`" v-if="this.id > 0">
|
||||||
|
<div class="inline-flex h-full px-3 py-1 space-x-1 rounded-md bg-lime-800 text-white items-center transition-all duration-300 ease-in-out hover:bg-lime-600" title="Back">
|
||||||
|
<span class="scale-100">
|
||||||
|
<ArrowLeftBold/>
|
||||||
|
</span>
|
||||||
|
<span class="uncial-antiqua">Back</span>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
<div class="inline-flex h-full px-3 py-1 space-x-1 rounded-md bg-lime-800 opacity-50 text-white items-center hover:cursor-not-allowed" v-else>
|
||||||
|
<span class="scale-100">
|
||||||
|
<ArrowLeftBold/>
|
||||||
|
</span>
|
||||||
|
<span class="uncial-antiqua">Back</span>
|
||||||
|
</div>
|
||||||
|
<router-link :to="`/quiz/question/${this.id+1}`" v-if="this.id < this.questionStore.questions.length - 1 && this.answersStore.isAnswered(this.id)">
|
||||||
|
<div class="inline-flex h-full px-3 py-1 space-x-1 rounded-md bg-lime-800 text-white items-center transition-all duration-300 ease-in-out hover:bg-lime-600" title="Next">
|
||||||
|
<span class="uncial-antiqua">Next</span>
|
||||||
|
<span class="scale-100">
|
||||||
|
<ArrowRightBold/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
<div class="inline-flex h-full px-3 py-1 space-x-1 rounded-md bg-lime-800 opacity-50 text-white items-center hover:cursor-not-allowed" v-else>
|
||||||
|
<span class="uncial-antiqua">Next</span>
|
||||||
|
<span class="scale-100">
|
||||||
|
<ArrowRightBold/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Transition
|
||||||
|
enter-active-class="transition-all duration-300 origin-center ease-in"
|
||||||
|
enter-from-class="transform opacity-0 md:scale-95"
|
||||||
|
leave-active-class="transition-all duration-300 origin-center ease-out"
|
||||||
|
leave-to-class="transform opacity-0 md:scale-95"
|
||||||
|
>
|
||||||
|
<div v-show="this.id == this.questionStore.questions.length -1 && this.answersStore.isAnswered(this.id)" class="mx-auto my-8 w-fit transition-all">
|
||||||
|
<button @click="this.submitAnswers()">
|
||||||
|
<div class="inline-flex px-3 py-1 space-x-2 rounded-md bg-lime-800 text-white transition-all duration-300 ease-in-out hover:bg-lime-600">
|
||||||
|
<span class="scale-75">
|
||||||
|
<FleurDeLis/>
|
||||||
|
</span>
|
||||||
|
<span class="uncial-antiqua">Submit Answers</span>
|
||||||
|
<span class="scale-75">
|
||||||
|
<FleurDeLis/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
Loading
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:focus + label>div {
|
||||||
|
@apply bg-lime-200;
|
||||||
|
@apply text-orange-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
:checked + label>div {
|
||||||
|
@apply border-lime-600;
|
||||||
|
@apply border-solid;
|
||||||
|
@apply border-l-2;
|
||||||
|
@apply text-orange-600;
|
||||||
|
}
|
||||||
|
:not(:checked):disabled + label>div {
|
||||||
|
@apply opacity-50;
|
||||||
|
@apply cursor-not-allowed;
|
||||||
|
@apply hover:bg-inherit;
|
||||||
|
@apply hover:text-inherit;
|
||||||
|
}
|
||||||
|
:checked:disabled + label>div {
|
||||||
|
@apply border-2;
|
||||||
|
@apply border-lime-600;
|
||||||
|
@apply border-solid;
|
||||||
|
}
|
||||||
|
</style>
|
61
client/src/views/quiz/Quiz.vue
Normal file
61
client/src/views/quiz/Quiz.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
import RoadVariant from '@/components/icons/RoadVariant.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'QuizConsole',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
RoadVariant,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Take the Quiz
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<article class="prose mx-auto">
|
||||||
|
<h2 class="uncial-antiqua" key="welcome-0">
|
||||||
|
Gather your Things
|
||||||
|
</h2>
|
||||||
|
<p class="text-leader" key="welcome-1">
|
||||||
|
We are about to embark upon a journey.
|
||||||
|
</p>
|
||||||
|
<p class="text-leader" key="welcome-2">
|
||||||
|
In a minute, I will ask you a series of multiple choice questions.
|
||||||
|
For most of them, you will need to select the one answer.
|
||||||
|
Some questions will need multiple answers.
|
||||||
|
Don’t over-think them!
|
||||||
|
</p>
|
||||||
|
<p class="text-leader" key="welcome-3">
|
||||||
|
<em class="uncial-antiqua">Wanderhome</em> is about going wherever the road takes you, and reflecting on how the journey changes us.
|
||||||
|
The road is full of myriad travellers, each with their rich lives and stories.
|
||||||
|
This quiz will help you figure out what kinds of stories you will bring to the table.
|
||||||
|
</p>
|
||||||
|
<p class="text-leader" key="welcome-4">
|
||||||
|
Will you join me?
|
||||||
|
</p>
|
||||||
|
<div class="w-fit mx-auto" key="welcome-5">
|
||||||
|
<router-link active-class="active-link" class="navlink" to="/quiz/question/0">
|
||||||
|
<div class="inline-flex px-3 py-1 space-x-2 rounded-md bg-lime-800 text-white transition-all duration-300 ease-in-out hover:bg-lime-600">
|
||||||
|
<span class="scale-75">
|
||||||
|
<RoadVariant/>
|
||||||
|
</span>
|
||||||
|
<span class="uncial-antiqua">Let’s Go</span>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
23
client/src/views/results/Answers.vue
Normal file
23
client/src/views/results/Answers.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
export default {
|
||||||
|
name: 'Answers',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Global Answers
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</template>
|
34
client/src/views/results/Index.vue
Normal file
34
client/src/views/results/Index.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script>
|
||||||
|
import Sidebar from '@/components/sidebar/Index.vue'
|
||||||
|
export default {
|
||||||
|
name: 'ResultsIndex',
|
||||||
|
components: {
|
||||||
|
Sidebar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col space-y-3 md:flex-row md:space-x-6 md:space-y-0">
|
||||||
|
<div class="flex flex-col w-full md:w-1/3 h-fit">
|
||||||
|
<Sidebar :prefix="$router.options.routes[3].path" :routes="$router.options.routes[3].children">
|
||||||
|
<template v-slot:header>
|
||||||
|
Results
|
||||||
|
</template>
|
||||||
|
</Sidebar>
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full md:w-2/3">
|
||||||
|
<router-view v-slot="{ Component, route }" class="w-full" ref="panel" appear>
|
||||||
|
<Transition mode="out-in"
|
||||||
|
:enter-active-class="route.meta.panelEnterActiveClass"
|
||||||
|
:enter-from-class="route.meta.panelEnterFromClass"
|
||||||
|
:leave-active-class="route.meta.panelLeaveActiveClass"
|
||||||
|
:leave-to-class="route.meta.panelLeaveToClass"
|
||||||
|
>
|
||||||
|
<component :is="Component" />
|
||||||
|
</Transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
78
client/src/views/results/Results.vue
Normal file
78
client/src/views/results/Results.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
return {
|
||||||
|
appStore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'Results',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderAnimals(rawText) {
|
||||||
|
var output = ''
|
||||||
|
var length = rawText.length
|
||||||
|
for (var index = 0; index < length; index++) {
|
||||||
|
var article = ['a','e','i','o','u'].includes(rawText[index].slice(0,1)) ? 'an' : 'a'
|
||||||
|
if (index == length - 2) {
|
||||||
|
var conjunction = ', or '
|
||||||
|
} else if (index == length -1) {
|
||||||
|
var conjunction = '.'
|
||||||
|
} else {
|
||||||
|
var conjunction = ', '
|
||||||
|
}
|
||||||
|
output += `${article} ${rawText[index]}${conjunction}`
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
<span v-if="this.appStore.results.playbooks.length > 1">Your Results</span>
|
||||||
|
<span v-else>Your Result</span>
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<article class="prose">
|
||||||
|
<section v-for="playbook in this.appStore.results.playbooks" :key="Object.keys(playbook)[0]" class="border-solid border-b-2 border-lime-600 border-opacity-25">
|
||||||
|
<h2 class="flex justify-between">
|
||||||
|
<span class="uncial-antiqua align-bottom">
|
||||||
|
<span class="text-xl">The</span> <span class="text-3xl">{{ Object.keys(playbook)[0].slice(0,1).toUpperCase()+Object.keys(playbook)[0].slice(1) }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-sm">Pages {{ Object.values(playbook)[0].pages }}</span>
|
||||||
|
</h2>
|
||||||
|
<p class="text-leader">
|
||||||
|
{{ Object.values(playbook)[0].flavour }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ Object.values(playbook)[0].blurb }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You are alive. Your care is <strong>{{ Object.values(playbook)[0].care }}</strong>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Your animal form is {{ renderAnimals(Object.values(playbook)[0].animals) }}
|
||||||
|
</p>
|
||||||
|
<h4>
|
||||||
|
You can always do the following:
|
||||||
|
</h4>
|
||||||
|
<ul>
|
||||||
|
<li class="my-0 py-0 mx-3" v-for="(action, index) in Object.values(playbook)[0].actions" :key="Object.keys(playbook)[0] + index" v-html="action"></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</template>
|
56
client/src/views/results/Scores.vue
Normal file
56
client/src/views/results/Scores.vue
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
return {
|
||||||
|
appStore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'Scores',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Scores
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
<p>
|
||||||
|
The following are your scores for each playbook:
|
||||||
|
</p>
|
||||||
|
<table class="table-auto w-full max-w-xs mx-auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Playbook
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Score
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(score, playbook) in this.appStore.results.all_playbooks" :key="playbook">
|
||||||
|
<td>
|
||||||
|
The {{ playbook.slice(0,1).toUpperCase() + playbook.slice(1) }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ Math.round(100*score/this.appStore.results.max_score) }} %
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</template>
|
23
client/src/views/results/Statistics.vue
Normal file
23
client/src/views/results/Statistics.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
import Content from '@/components/Content.vue'
|
||||||
|
import Header from '@/components/Header.vue'
|
||||||
|
import TextFrame from '@/components/TextFrame.vue'
|
||||||
|
export default {
|
||||||
|
name: 'Statistics',
|
||||||
|
components: {
|
||||||
|
Content,
|
||||||
|
Header,
|
||||||
|
TextFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<TextFrame>
|
||||||
|
<Header>
|
||||||
|
Statistics
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
|
||||||
|
</Content>
|
||||||
|
</TextFrame>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user