| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- <template lang="pug">
- main.view--survey.f-col.start.w-full
- header.w-full.f-col
- p survey for profile: {{ current }}
- article.match.w-full
- ul.w-full
- template(v-for="(q, i) in profileQuestions" :key="q.response_key_prompt")
- li(v-if="step == i + 1")
- p {{q.response_key_category}}:
- span in db:
- span(v-if="q.response_key_id") true - id:{{q.response_key_id}} |
- span(v-else) false |
- span {{q.response_key_prompt}}?
- span {{q.response_key_description}}
-
- //- Select
- div(v-if="q.responses.length")
- button(
- v-for="(res, index) in q.responses"
- :key="index"
- @click="storeResponseLike(step, q.response_key_id, q.response_key_prompt, res); step++"
- ).p-0 {{res}}
-
- //- Fill in the blank
- div(v-else-if="q.response_key_category === 'profile'")
- input(@input="storeResponseLike(step, q.response_key_id, q.response_key_prompt, profile[q.response_key_prompt])" v-model="profile[q.response_key_prompt]" @keyup.enter="step++")
- label >{{ profile[q.response_key_prompt]}}
-
- //- Aspects
- div(v-else).f-col
- input(type="range" min="-3" max="3" list="ticks" @input="storeResponseLike(step, q.response_key_id, q.response_key_prompt, aspects[q.response_key_category])" v-model="aspects[q.response_key_category]").w-full
- label {{ aspectResponses[parseInt(aspects[q.response_key_category]) + 3] }}
-
- nav.f-row
- button(:disabled="step == 0" @click="step--") back
- p {{step}} of {{profile.length}}
- button(
- v-if="(q.response_key_category === 'profile')"
- @click="storeResponseLike(step, q.response_key_id, q.response_key_prompt, profile[q.response_key_prompt]); step++"
- ) next
- button(
- v-else
- @click="storeResponseLike(step, q.response_key_id, q.response_key_prompt, aspects[q.response_key_category]); step++"
- ) next
-
- //- Confirmation
- li(v-if="step == profileQuestions.length + 1")
- p Does this look correct?
- h4 {{ profile }}
- h4 {{aspects}}
- nav.f-row
- button(@click="step--") back
- p(@click="step = 1").p-1 start over
- button(@click="onSave") save
- //- button(@click="$router.push({ name: 'HomeView' })") save
- footer
- button(@click="bypass") +30 user profiles
- </template>
-
- <script>
- import { surveyFactory, randomSurveyResponses } from '@/utils'
- import { allSteps, aspectResponses, possible } from '@/utils/lang'
-
- import { currentProfile, createProfileForUserId, fetchProfileByProfileId, scoreSurveyByProfileId, signupUser } from '@/services'
-
- import { maxDistanceKey } from '../../../backend/db/data-generator/config.json'
-
- export default {
- props: {
- pid: {
- type: Number,
- },
- },
- data() {
- return {
- bypassCount: 0,
- validSurvey: null,
- step: 1,
- responseLikes: {},
- profile: {},
- aspects: {
- grit: 0,
- openness: 0,
- bravery: 0,
- empathy: 0,
- honesty: 0,
- respect: 0,
- },
- aspectResponses: Object.values(aspectResponses.usa)
- }
- },
- computed: {
- current() {
- return currentProfile
- },
- profileQuestions() {
- if (!this.validSurvey) return []
- return this.validSurvey.steps
- },
- },
- async created() {
- /**
- * Before this ever gets called, surveyFactory.questionsFromDb
- * must be set by App.created()
- */
- this.validSurvey = await surveyFactory.createSurvey(
- allSteps['usa'],
- possible['usa']['roles'],
- )
- },
- methods: {
- // Just for testing quicker
- bypass() {
- const n = 30
- const toGen = [...Array(n).keys()]
- toGen.forEach(async () => {
- const randRes = randomSurveyResponses(this.bypassCount)
- randRes.forEach((tr, i) => {
- this.storeResponseLike(i, tr.idOrPrompt, tr.idOrPrompt, tr.val)
- })
- this.bypassCount++
- await this.onSave()
- })
- this.step = 21
- console.warn(`Auto-filling survey ${n} times...`)
- },
- storeResponseLike(step, id, prompt, val) {
- // Also update the form model state for now
- // We probably can refactor this out
- this.profile[prompt] = val
-
- /**
- * If NO response id is present, that means the answer
- * is required and associated with a User and NOT a Profile
- **/
- this.responseLikes[`step-${step}`] = {
- idOrPrompt: id ? id : prompt,
- val: val.toString()
- }
- },
- _formatIntoResponses(survey) {
- return survey.map(resLike => {
- resLike.response_key_id = resLike.idOrPrompt
- delete resLike.idOrPrompt
- return resLike
- })
- },
- /**
- * Survey responses have User information
- * mixed in with scoring information so we
- * need to separate them
- * @param {array} responseLikes answered by the survey
- */
- _separateUserInfoFromResponses(responseLikes) {
- let survey = []
- const user = {}
- responseLikes.forEach(resLike => {
- if(resLike.idOrPrompt && Number.isFinite(parseInt(resLike.idOrPrompt))) {
- survey.push(resLike)
- } else {
- user[resLike.idOrPrompt] = resLike.val
- }
- })
- return [user, this._formatIntoResponses(survey)]
- },
- async _createUserProfileRel(user, survey) {
- /** A User is required before creating a profile */
- const createdUser = await signupUser(user)
- if(!createdUser) return
-
- const userProfileRel = await createProfileForUserId(createdUser.user_id, survey)
- if(!userProfileRel) return
-
- return userProfileRel
- },
- async _getProfileWithScore(createdProfileId, maxDistance) {
- /** A Profile is associated with n:1 user and referenced by profile id */
- const fetchedProfile = await fetchProfileByProfileId(createdProfileId)
- if(!fetchedProfile) {
- console.error(`Could not fetch Profile ${createdProfileId}. Please check that a User and Profile were created.`)
- return
- }
-
- /** Use profile answers to compare against other profile answers */
- const scored = await scoreSurveyByProfileId(createdProfileId, maxDistance)
- if(!scored) {
- console.error(`Could not score Profile ${createdProfileId}.`)
- return
- }
- return fetchedProfile
- },
- async _setLoginForProfile(profile) {
- const currentId = currentProfile.login(profile.profile_id)
- if(currentId && profile.responses.length) {
- // Stores responses without fetching again
- currentProfile.setResponses(profile.responses)
- }
- if(!currentProfile.isComplete) {
- console.error(`Profile ${currentProfile.id} is incomplete. Please make sure all survey questions have been answered.`)
- return
- }
- },
- async onSave() {
- const [ user, survey] = this._separateUserInfoFromResponses(Object.values(this.responseLikes))
- const maxDistanceRes = survey.find(res => res.response_key_id == maxDistanceKey)
-
- /**
- * Creating a profile only returns the created
- * user id and profile id
- **/
- const userProfileRel = await this._createUserProfileRel(user, survey)
- const createdProfileId = userProfileRel.profile_id
-
- /** A Profile is associated with n:1 user and referenced by profile id */
- const fetchedProfile = await this._getProfileWithScore(createdProfileId, maxDistanceRes.val)
-
- /**
- * Login only after there is a user and
- * that user has a profile and all responses
- */
- this._setLoginForProfile(fetchedProfile)
-
- this.$router.push({ name: 'HomeView' })
- },
- back(prompt) {
- this[prompt] = ''
- this.step--
- },
- },
- }
- </script>
-
- <style lang="postcss">
- .slide-up
- &-leave, &-enter
- &-active
- transition-delay: 500ms
- transition-property: opacity, background-color, font-size, transform, color
- transition-timing-function: cubic-bezier(1, 0.3, 0.5, 1)
- transition-duration: 300ms
- &-enter-from, &-leave-to
- opacity: 0
-
- .fade
- animation: fadeIn 1200ms ease-in
-
- @keyframes fadeIn
- 0%
- opacity: 0
- 50%
- opacity: 0.5
- 100%
- opacity:1
-
- h1
- color: white
-
- main
- padding: 5vh
- display: flex
- header
- p
- color: #666
- line-height: 1.3em
- color: #ccc
- font-style: italic
- text-align: left
-
- article
- height: 100%
- width: 100%
- flex-direction: column
- ul
- height: 100%
- list-style: none
- button
- padding: 1em
- p
- padding: 1em
- color: white
- .form
- border: 1px solid #fff
- width: 100%
- header, footer, ul
- padding: 1vh
- ul
- display: flex
- width: 100%
- > li
- flex-direction: column
- border: 0px solid yellow
- width: 100%
- padding: 0
- text-align: left
- label
- margin-right: 1vh
- > h3
- text-transform: capitalize
- padding: 1vh 0
- > div > span
- display: block
- text-align: center
- </style>
|