You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <template lang="pug">
  2. .form.f-col.start.mr-1.w-full(v-if='form.length')
  3. header
  4. p answers to save: {{ answers }}
  5. ul.w-full(v-for='(step, i) in form')
  6. li.f-row.start.b-solid.p-0(
  7. v-for='prompt in step'
  8. v-if='step && step.length'
  9. v-show='(i + 1) == state.step'
  10. )
  11. h3 {{ prompt.question }}?
  12. .response-wrapper(v-if='prompt.type === "input-string"')
  13. label {{ prompt.type }}
  14. input(v-model='answers[makeKebob(prompt.question)]')
  15. .response-wrapper(v-else-if='prompt.type === "tag-cloud"')
  16. label {{ prompt.type }}
  17. button.p-0(
  18. :disabled='response == answers[makeKebob(prompt.question)]'
  19. :prompt-question='makeKebob(prompt.question)'
  20. @click='respondFromTag'
  21. v-for='response in prompt.responses'
  22. ) {{ response }}
  23. footer.f-row.w-full
  24. button.p-1(:disabled='state.step == 1' @click='back') back
  25. button.p-1(:disabled='state.step == form.length' @click='next') next
  26. button.p-1(:disabled='state.step != form.length' @click='next') save
  27. .f-grow
  28. p step: {{ state.step }} of {{ form.length }}
  29. </template>
  30. <script setup>
  31. import Joi from 'joi'
  32. import { validatorMapping, makeKebob } from '@/utils'
  33. import { defineProps, reactive } from 'vue'
  34. const props = defineProps({
  35. form: {
  36. type: Array,
  37. required: true,
  38. },
  39. })
  40. /**
  41. * Our form is comprised of steps, and each step has a series of questions
  42. */
  43. const state = reactive({ step: 1 })
  44. /**
  45. * Create state object to store answers based on the questions in formSteps
  46. */
  47. const answers = reactive({})
  48. const resetAnswers = () => {
  49. Object.keys(answers).forEach(key => (answers[key] = null))
  50. }
  51. resetAnswers()
  52. /**
  53. * Callback for clicking a tag to respond
  54. */
  55. const respondFromTag = e => {
  56. const answer = e.target.textContent
  57. const questionKey =
  58. e.target.attributes.getNamedItem('prompt-question').nodeValue
  59. answers[questionKey] = answer
  60. }
  61. /**
  62. * Check answered fields in current step, build a validator, then validate the answers by prompt.type
  63. * @param {number} step // The current step
  64. * @returns {object} // Joi object
  65. */
  66. const isValid = step => {
  67. const schema = {}
  68. const answeredThisStep = {}
  69. props.form[step].forEach(prompt => {
  70. const key = makeKebob(prompt.question)
  71. schema[key] = validatorMapping[prompt.type]
  72. answeredThisStep[key] = answers[key]
  73. })
  74. return Joi.object(schema).validate(answeredThisStep)
  75. }
  76. /**
  77. * Save or take the-nNext step in the form
  78. */
  79. const next = e => {
  80. const validity = isValid(state.step - 1)
  81. if (validity.error) return console.error(validity.error)
  82. // Save or next
  83. if (state.step === props.form.length) {
  84. alert('saved...')
  85. resetAnswers()
  86. state.step = 1
  87. } else if (state.step < props.form.length) {
  88. state.step++
  89. } else {
  90. console.error(
  91. `Cannot perform action: next() | on form step: ${state.step} of ${props.formSteps.length}`,
  92. )
  93. }
  94. }
  95. /**
  96. * Back a step in the form
  97. */
  98. const back = e => {
  99. if (state.step > 1) {
  100. state.step--
  101. } else {
  102. console.error(
  103. `Cannot perform action: back() | on form step: ${state.step} of ${props.formSteps.length}`,
  104. )
  105. }
  106. }
  107. </script>
  108. <style lang="postcss">
  109. // prettier-ignore
  110. @import '@/sss/theme.sss'
  111. .form
  112. color: $light
  113. ul
  114. list-style: none
  115. </style>