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 { 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 AboutPage from '@/views/about/About.vue'
|
||||
import Acknowledgements from '@/views/about/Acknowledgements.vue'
|
||||
import Wanderhome from '@/views/about/Wanderhome.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 = [
|
||||
{
|
||||
@ -26,8 +35,33 @@ const routes = [
|
||||
index: 1,
|
||||
svgPath: mdiAccountQuestion,
|
||||
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',
|
||||
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) => {
|
||||
const enterActiveClass = 'transition-all duration-300 ease-in'
|
||||
const leaveActiveClass = 'transition-all duration-300 ease-out'
|
||||
const enterActiveClass = 'transition-all duration-300 origin-top ease-in'
|
||||
const leaveActiveClass = 'transition-all duration-300 origin-top ease-out'
|
||||
const baseFromClass = 'transform opacity-0 md:scale-75'
|
||||
if ( to.fullPath.split('/')[1] !== from.fullPath.split('/')[1] || !from.matched.length ) {
|
||||
const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.meta.index
|
||||
|
@ -1,19 +1,24 @@
|
||||
<script>
|
||||
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'
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
AccountQuestion,
|
||||
Content
|
||||
Content,
|
||||
Header,
|
||||
TextFrame
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Content>
|
||||
<template v-slot:header>
|
||||
<TextFrame>
|
||||
<Header>
|
||||
home
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
</Header>
|
||||
<Content>
|
||||
<article class="prose mx-auto">
|
||||
<p class="text-leader">
|
||||
The road ahead is long and meandering.
|
||||
@ -28,11 +33,11 @@
|
||||
</p>
|
||||
<div class="w-fit mx-auto">
|
||||
<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">
|
||||
<AccountQuestion/>
|
||||
</span>
|
||||
<span>Take the Quiz</span>
|
||||
<span class="uncial-antiqua">Take the Quiz</span>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
@ -42,6 +47,6 @@
|
||||
I would nevertheless implore you to play <em>Wanderhome</em> if you haven’t already.
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
</Content>
|
||||
</TextFrame>
|
||||
</template>
|
@ -1,18 +1,22 @@
|
||||
<script>
|
||||
import Content from '@/components/Content.vue'
|
||||
import Header from '@/components/Header.vue'
|
||||
import TextFrame from '@/components/TextFrame.vue'
|
||||
export default {
|
||||
name: 'About',
|
||||
components: {
|
||||
Content
|
||||
Content,
|
||||
Header,
|
||||
TextFrame
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div >
|
||||
<Content>
|
||||
<template v-slot:header>
|
||||
<TextFrame>
|
||||
<Header>
|
||||
Background
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
</Header>
|
||||
<Content>
|
||||
<article class="prose mx-auto">
|
||||
<h2>What Exactly Is This?</h2>
|
||||
<p>
|
||||
@ -87,9 +91,8 @@
|
||||
It will apply all the things I currently know, and also add websockets and state synchronisation to the mix.
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
</Content>
|
||||
</div>
|
||||
</TextFrame>
|
||||
</template>
|
||||
<style scoped>
|
||||
</style>
|
@ -1,18 +1,23 @@
|
||||
<script>
|
||||
import Content from '@/components/Content.vue'
|
||||
import Header from '@/components/Header.vue'
|
||||
import TextFrame from '@/components/TextFrame.vue'
|
||||
export default {
|
||||
name: 'Acknowledgements',
|
||||
components: {
|
||||
Content
|
||||
Content,
|
||||
Header,
|
||||
TextFrame
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<Content>
|
||||
<template v-slot:header>
|
||||
<TextFrame>
|
||||
<Header>
|
||||
Acknowledgements
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
</Header>
|
||||
<Content>
|
||||
<article class="prose mx-auto">
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
</Content>
|
||||
</TextFrame>
|
||||
</div>
|
||||
</template>
|
@ -1,9 +1,8 @@
|
||||
<script>
|
||||
import Content from '@/components/Content.vue'
|
||||
import Sidebar from '@/components/sidebar/Index.vue'
|
||||
export default {
|
||||
name: 'AboutIndex',
|
||||
components: {
|
||||
Content,
|
||||
Sidebar
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
<script>
|
||||
import Content from '@/components/Content.vue'
|
||||
import Header from '@/components/Header.vue'
|
||||
import TextFrame from '@/components/TextFrame.vue'
|
||||
export default {
|
||||
name: 'Wanderhome',
|
||||
components: {
|
||||
Content
|
||||
Content,
|
||||
Header,
|
||||
TextFrame
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<Content>
|
||||
<template v-slot:header>
|
||||
<TextFrame>
|
||||
<Header>
|
||||
About <em>Wanderhome</em>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
</Header>
|
||||
<Content>
|
||||
<article class="prose mx-auto">
|
||||
<p>
|
||||
<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!
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
</Content>
|
||||
</TextFrame>
|
||||
</div>
|
||||
</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