| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- <template lang="pug">
- .form.f-col.start.mr-1.w-full(v-if="form.length")
- header
- p answers to save: {{ answers }}
-
- ul.w-full(v-for="(step, i) in form")
- li.f-row.start.b-solid.p-0(
- v-for="prompt in step"
- v-if="step && step.length"
- v-show="(i + 1) == state.step"
- )
- h3 {{ prompt.question }}?
- .response-wrapper(v-if="prompt.type === 'input-string'")
- label {{ prompt.type }}
- input(v-model="answers[makeKey(prompt)]")
- .response-wrapper(v-else-if="prompt.type === 'tag-cloud'")
- label {{ prompt.type }}
- button.p-0(
- :disabled="response == answers[makeKey(prompt)]"
- :prompt-question="makeKey(prompt)"
- @click="respondFromTag"
- v-for="response in prompt.responses"
- ) {{ response }}
- //- Checklist
- .response-wrapper(v-if="prompt.type === 'checklist'")
- .checklist(
- v-for="(response, index) in prompt.responses"
- v-bind:key="response")
- input(
- type="checkbox"
- :id="response"
- :value="response"
- :name="response"
- :true-value="response"
- false-value=""
- v-model="answers[makeKey(prompt)]")
- label(:for="response") {{ response }}
- //- Slider from -3 to 0 to +3 (increments of 1)
- .response-wrapper(v-else-if="prompt.type === 'input-slide'")
- label {{ prompt.type }}
- input(
- type="range"
- min="-3"
- max="3"
- :prompt="makeKey(prompt)"
- :value="answers[makeKey(prompt)]"
- @input="setAnswer"
- )
- span {{ answers[makeKey(prompt)] }}
-
- footer.f-row.w-full
- button.p-1(:disabled="state.step == 1" @click="back") back
- button.p-1(:disabled="state.step == form.length" @click="next") next
- button.p-1(:disabled="state.step != form.length" @click="next") save
- .f-grow
- p step: {{ state.step }} of {{ form.length }} for {{ pid }}
- </template>
-
- <script setup>
- import Joi from 'joi'
- import { reactive } from 'vue'
- import { useRouter } from 'vue-router'
- import { validatorMapping, makeKebob } from '@/utils'
- import {
- saveSurveyByProfileId,
- scoreSurveyByProfileId,
- } from '../services/survey.service'
- import { scoreVals } from '../../../backend/db/data-generator/config.json'
-
- const props = defineProps({
- form: {
- type: Array,
- required: true,
- },
- pid: {
- required: true,
- type: Number,
- },
- })
- const makeKey = prompt => {
- return `${prompt.id}-${prompt.type}-${makeKebob(prompt.question)}`
- }
-
- /**
- * Our form is comprised of steps, and each step has a series of questions
- */
- const state = reactive({ step: 1 })
-
- /**
- * Create state object to store answers based on the questions in formSteps
- */
- const answers = reactive({})
-
- const setAnswer = e => {
- const prompt = e.target.attributes.prompt.nodeValue
- answers[prompt] = e.target.value
- }
- const resetAnswers = () => {
- Object.keys(answers).forEach(key => (answers[key] = null))
- }
- resetAnswers()
- const setupAnswers = () => {
- props.form.forEach(step => {
- step.forEach(prompt => {
- answers[makeKey(prompt)] = '0'
- })
- })
- }
- setupAnswers()
- /**
- * Callback for clicking a tag to respond
- */
- const respondFromTag = e => {
- const answer = e.target.textContent
- const questionKey =
- e.target.attributes.getNamedItem('prompt-question').nodeValue
- answers[questionKey] = answer
- }
- /**
- * Check answered fields in current step, build a validator, then validate the answers by prompt.type
- * @param {number} step // The current step
- * @returns {object} // Joi object
- */
- const isValid = step => {
- const schema = {}
- const answeredThisStep = {}
- props.form[step].forEach(prompt => {
- const key = makeKey(prompt)
- schema[key] = validatorMapping[prompt.type]
- answeredThisStep[key] = answers[key]
- })
- return Joi.object(schema).validate(answeredThisStep)
- }
- /**
- * Vary the final answer format by input type
- */
- const formatAnswer = (inputType, answer) => {
- let formattedAnswer = answer
- if (inputType == 'slide') {
- const offset = (scoreVals.length - 1) / 2
- formattedAnswer = scoreVals[offset + parseInt(answer)].toString()
- }
- return formattedAnswer
- }
- /**
- * Save or take the-nNext step in the form
- */
- const getInputTypeFromKey = k => {
- const inputIndex = k.split('-').indexOf('input')
- return k.split('-')[inputIndex + 1]
- }
-
- const next = async e => {
- const validity = isValid(state.step - 1)
- if (validity.error) console.error(validity.error)
-
- if (state.step === props.form.length) {
- // Make key map of each prompt.id
- const questiontoResponseKeyId = {}
- props.form.forEach(step => {
- step.forEach(prompt => {
- questiontoResponseKeyId[makeKey(prompt)] = prompt.id
- })
- })
-
- const idWithResponseVal = Object.keys(answers).map(answerKey => {
- return {
- response_key_id: questiontoResponseKeyId[answerKey],
- val: formatAnswer(
- getInputTypeFromKey(answerKey),
- answers[answerKey],
- ),
- }
- })
-
- const maxDistance = 100
-
- if (!props.pid) return console.error(`no pid: ${props.pid}`)
-
- saveSurveyByProfileId(idWithResponseVal, props.pid)
- resetAnswers()
-
- scoreSurveyByProfileId(props.pid, maxDistance)
-
- state.step = 1
-
- const router = useRouter()
- router.push({ name: 'HomeView' })
- } else if (state.step < props.form.length) {
- state.step++
- } else {
- console.error(
- `Cannot perform action: next() | on form step: ${state.step} of ${props.formSteps.length}`,
- )
- }
- }
- /**
- * Back a step in the form
- */
- const back = e => {
- if (state.step > 1) {
- state.step--
- } else {
- console.error(
- `Cannot perform action: back() | on form step: ${state.step} of ${props.formSteps.length}`,
- )
- }
- }
- </script>
-
- <style lang="postcss">
- // prettier-ignore
- @import '@/sss/theme.sss'
-
- .form
- color: $light
- ul
- list-style: none
- </style>
|