# ZeenSurveyModal

Компонент опроса

# Example

# Slots

Название Описание Обязательный По умолчанию
startHeader Верхний блок стартового окна - -
endHeader Верхний блок окна результата - -
subTitle Подзаголовок (принимает объект #subTitle='{question}' для фильтрации по типу вопроса question.type: rates, multiple_rates, checkboxes, radio, input) - -

# Props

Название Тип Обязательный По умолчанию Описание
survey Object + Данные опроса
name Boolean - 'survey-modal' Имя модалки голосования
buttonText Object - { start: 'Начать', next: 'Далее', finally: 'Закрыть', } Объект с текстами кнопок
rateText Object - { low: 'плохо', high: 'хорошо', } Объект с текстами описания границ рейтинга
maxRate Number - 5 Максимальная оценка (рейтинг)
resultTitle String - 'Спасибо за обратную связь!' Текст результата
textAreaPlaceholder String - Пустая строка Текст плейсхолдера для полей ввода
isComplete Boolean - false Пройден ли опрос юзером

# Source Code

<template lang="pug">
  ZeenModal(:name='name' size='large' v-on='modalEvents')
    .zeen-survey
      .zeen-survey__wrapper(v-if='isFirstStep')
        .zeen-survey__header
          slot(name='startHeader')
        ZeenHeadline.zeen-survey__main-title(tag='h4' v-html='surveyData.title')
        .zeen-survey__btn
          ZeenButton(
            @click.prevent='next'
            :disabled='isButtonDisabled'
          ) {{ buttonText.start }}

      .zeen-survey__wrapper-list(v-else-if='isQuestionStep')
        .zeen-survey__wrapper
          .zeen-survey__top
            .zeen-survey__step-number {{step}}/{{surveyData.survey_questions.length}}
            ZeenHeadline(tag='h3') {{question.title}}
            .zeen-survey__sub-title
              slot(name='subTitle' :question='question')

          //- rates
          .zeen-survey__rates-wrapper(v-if='isQuestionTypeRates')
            .zeen-survey__rates
              .zeen-survey__rate(
                v-for='rate in rates'
                :key='rate'
                :class='getRatesClass(rate)'
                @click.prevent='setRateAnswer(question, rate)'
              ) {{rate}}
            .zeen-survey__rates-description
              p {{rateText.low}}
              p {{rateText.high}}

          //- multiple_rates
          .zeen-survey__multiple-rates-wrapper(v-else-if='isQuestionTypeMultipleRates')
            .zeen-survey__multiple-rates(v-for='item in question.survey_answers' :key='item.id')
              p.zeen-survey__multiple-rates-title(v-html='item.title')
              .zeen-survey__multiple-rates-inner
                .zeen-survey__multiple-rate(
                  v-for='rate in rates'
                  :key='rate'
                  :class='{"zeen-survey__multiple-rate--active": checkMultipleRateActive(item.id, rate)}'
                  @click.prevent='setMultipleRateAnswer(question, item.id, rate)'
                ) {{rate}}

          //- checkboxes
          .zeen-survey__answers(v-else-if='isQuestionTypeCheckboxes')
            .zeen-survey__answer(v-for='answer in question.survey_answers' :key='answer.id')
              ZeenCheckbox(v-model='answer.value' :label='answer.title' :value='answer.value' @change='checkCheckboxValidation(question.survey_answers)')

          //- radio
          .zeen-survey__answers(v-else-if='isQuestionTypeRadio')
            .zeen-survey__answer(v-for='answer in question.survey_answers' :key='answer.id')
              ZeenRadio(v-model='activeAnswer' :value='answer.id' :name='question.id' :label='answer.title' @change='setRadioAnswer(question.id, answer.id, answer.title)')

          //- input
          .zeen-survey__answers(v-else-if='isQuestionTypeInput')
            .zeen-survey__answer
              ZeenTextArea(:placeholder="textAreaPlaceholder" :value='activeAnswerInput' @input='setInputAnswer')

          .zeen-survey__btn
            ZeenButton(
              :disabled='isButtonDisabled'
              @click.prevent='nextQuestion(question)'
            ) {{ buttonText.next }}

      //- results
      .zeen-survey__wrapper(v-else)
        .zeen-survey__header.zeen-survey__header--end
          slot(name='endHeader')
        ZeenHeadline.zeen-survey__main-title(tag='h3' v-html='resultTitle')
        .zeen-survey__btn
          ZeenButton(
            @click.prevent='closeModal'
          ) {{ buttonText.finally }}

</template>

<script>
import ZeenHeadline from '../ZeenHeadline'
import ZeenButton from '../ZeenButton'
import ZeenModal from '../ZeenModal'
import {createEventsFor} from '../../helpers/createBlockData'

export default {
  name: 'ZeenSurvey',
  components: {
    ZeenHeadline,
    ZeenButton,
    ZeenModal,
  },
  props: {
    name: {
      type: String,
      default: 'survey-modal',
    },
    survey: {
      type: Object,
      default: () => {
        return {}
      },
    },
    buttonText: {
      type: Object,
      default: () => {
        return {
          start: 'Начать',
          next: 'Далее',
          finally: 'Закрыть',
        }
      },
    },
    rateText: {
      type: Object,
      default: () => {
        return {
          low: 'плохо',
          high: 'хорошо',
        }
      },
    },
    maxRate: {
      type: Number,
      default: 5,
    },
    resultTitle: {
      type: String,
      default: 'Спасибо за обратную связь!',
    },
    textAreaPlaceholder: {
      type: String,
      default: '',
    },
    isComplete: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      isButtonDisabled: false,
      step: 0,
      surveyResults: [],
      activeRateValue: 0,
      // Переменные для хранения временных результатов разных типов
      activeAnswer: {},
      activeAnswerMultiple: [],
      activeAnswerInput: '',
    }
  },
  methods: {
    next() {
      this.step++

      this.isButtonDisabled = true
    },
    nextQuestion(question) {
      this.saveResult(question)

      if (this.step === this.surveyData.survey_questions.length) {
        this.saveData()
      }

      this.next()
    },
    saveResult(question) {
      switch (question.type) {
        case 'checkboxes': {
          question.survey_answers.forEach((item) => {
            if (item.value) {
              this.addResult(question.id, item.id, item.title)
            }
          })

          break
        }
        case 'input': {
          this.addResult(question.id, question.survey_answers[0].id, this.activeAnswerInput)

          this.activeAnswerInput = ''

          break
        }
        case 'multiple_rates': {
          this.surveyResults = this.surveyResults.concat(this.activeAnswerMultiple)

          this.activeAnswerMultiple = []

          break
        }
        default: {
          this.surveyResults.push(this.activeAnswer)

          this.activeAnswer = {}
          this.activeRateValue = 0
        }
      }
    },
    addResult(questionId, answerId, value) {
      this.surveyResults.push({
        survey_question_id: questionId,
        survey_answer_id: answerId,
        answer: value,
      })
    },
    checkCheckboxValidation(answers) {
      this.isButtonDisabled = true

      answers.forEach((item) => {
        if (item.value === true) {
          this.isButtonDisabled = false
        }
      })
    },
    getQuestionType(question) {
      const answer = question.multiple_answer
      const type = question.survey_answers[0].answer_type

      const isRates = !answer && type === 'rating'
      const isMultipleRates = answer && type === 'rating'
      const isCheckboxes = answer && type === 'boolean'
      const isRadio = !answer && type === 'boolean'
      const isInput = !answer && type === 'input'

      switch (true) {
        case isRates: {
          return 'rates'
        }
        case isMultipleRates: {
          return 'multiple_rates'
        }
        case isCheckboxes: {
          question.survey_answers.forEach((item) => {
            item.value = false
          })

          return 'checkboxes'
        }
        case isRadio: {
          return 'radio'
        }
        case isInput: {
          return 'input'
        }
        default: {
          return 'error'
        }
      }
    },
    getRatesClass(rate) {
      return this.activeRateValue >= rate ? 'zeen-survey__rate--active' : ''
    },
    setRateAnswer(question, rate) {
      this.activeRateValue = rate

      this.activeAnswer = {
        survey_question_id: question.id,
        survey_answer_id: question.survey_answers[0].id,
        answer: rate,
      }

      this.isButtonDisabled = false
    },
    setMultipleRateAnswer(question, answerId, rate) {
      const result = {
        survey_question_id: question.id,
        survey_answer_id: answerId,
        answer: rate,
      }

      const resultInArray = this.activeAnswerMultiple.find((item) => item.survey_answer_id === result.survey_answer_id)

      if (resultInArray) {
        this.activeAnswerMultiple.splice(this.activeAnswerMultiple.indexOf(resultInArray), 1)
      }

      this.activeAnswerMultiple.push(result)

      this.activeAnswerMultiple = [...this.activeAnswerMultiple]

      if (question.survey_answers.length === this.activeAnswerMultiple.length) {
        this.isButtonDisabled = false
      }
    },
    setInputAnswer(value) {
      this.activeAnswerInput = value

      if (this.activeAnswerInput.trim()) {
        this.isButtonDisabled = false
      } else {
        this.isButtonDisabled = true
      }
    },
    setRadioAnswer(questionId, answerId, value) {
      this.activeAnswer = {
        survey_question_id: questionId,
        survey_answer_id: answerId,
        answer: value,
      }

      this.isButtonDisabled = false
    },
    saveData() {
      this.$emit('send', this.surveyResults)
    },
    closeModal() {
      this.$vfm.hide(this.name)
    },
    checkMultipleRateActive(id, rate) {
      return this.activeAnswerMultiple.find((item) => item.survey_answer_id === id)?.answer >= rate
    },
  },
  computed: {
    modalEvents() {
      return createEventsFor(this, 'modal_')
    },
    surveyData() {
      const surveyData = JSON.parse(JSON.stringify(this.survey))

      surveyData?.survey_questions?.forEach((item) => {
        item.type = this.getQuestionType(item)
      })

      return surveyData
    },
    rates() {
      let rates = [...Array(this.maxRate + 1).keys()]

      rates.shift()

      return rates
    },
    question() {
      return this.surveyData.survey_questions[this.step - 1]
    },
    isFirstStep() {
      return !this.isComplete && this.step === 0
    },
    isQuestionStep() {
      return !this.isComplete && this.step <= this.surveyData?.survey_questions?.length
    },
    isQuestionTypeRates() {
      return this.question.type === `rates`
    },
    isQuestionTypeMultipleRates() {
      return this.question.type === `multiple_rates`
    },
    isQuestionTypeCheckboxes() {
      return this.question.type === `checkboxes`
    },
    isQuestionTypeRadio() {
      return this.question.type === `radio`
    },
    isQuestionTypeInput() {
      return this.question.type === `input`
    },
  },
  watch: {},
}
</script>

<style lang="scss">
@import '/src/styles/mixins.scss';

:root {
  /* Размеры */
  --zeen-survey-rate-size: 40px;
  --zeen-survey-rate-gap: 20px;

  @include phones {
    --zeen-survey-rate-size: 30px;
    --zeen-survey-rate-gap: 10px;
  }

  /* Цвета */
  --zeen-survey-main-color: var(--main-light);
  --zeen-survey-top-color: var(--gray-1);
  --zeen-survey-rate-background: var(--gray-3);
  --zeen-survey-rate-active-background: var(--main-color);
  --zeen-survey-rate-color: var(--input-main-value-color);
  --zeen-survey-rate-active-color: var(--main-light);
  --zeen-survey-step-number-color: var(--gray-4);
  --zeen-survey-rates-descroption-color: var(--input-main-value-color);
}
</style>

<style lang="scss" scoped>
@import '../../styles/mixins.scss';

* {
  margin: 0;
  padding: 0;
}

::v-deep {
  .modal__content {
    padding: 0;
    overflow: hidden;
  }

  .modal__description {
    margin: 0;
  }
}

.zeen-survey {
  &__main-title,
  &__top {
    padding: 50px 50px 30px;
    background: var(--zeen-survey-top-color);

    @include phones-small {
      padding: 40px 30px 30px;
    }
  }

  &__main-title {
    background: transparent;
  }

  &__btn {
    margin-left: 50px;
    margin-bottom: 50px;

    @include phones-small {
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0;
      margin-bottom: 30px;
    }
  }

  &__step-number {
    font-style: normal;
    font-weight: normal;
    font-size: 14px;
    line-height: 22px;
    display: flex;
    align-items: center;
    color: var(--zeen-survey-step-number-color);
    margin-bottom: 10px;
  }

  &__header {
    display: flex;
    justify-content: center;
    align-items: center;
    background: var(--zeen-survey-top-color);
  }

  &__answers {
    padding: 30px 50px 10px;

    @include phones-small {
      padding: 30px 30px 10px;
    }
  }

  &__answer {
    margin-bottom: 20px;
  }

  // rates

  &__rates-wrapper {
    width: fit-content;
  }

  &__rates {
    display: flex;
    align-items: center;
    width: fit-content;
    padding: 40px 50px 10px;
    flex-wrap: wrap;

    @include phones-small {
      padding: 40px 30px 10px;
    }
  }

  &__rate {
    display: flex;
    justify-content: center;
    align-items: center;
    width: var(--zeen-survey-rate-size);
    height: var(--zeen-survey-rate-size);
    background: var(--zeen-survey-rate-background);
    color: var(--zeen-survey-rate-color);
    border-radius: 50%;
    cursor: pointer;
    margin-right: var(--zeen-survey-rate-gap);

    &--active {
      color: var(--zeen-survey-rate-active-color);
      background: var(--zeen-survey-rate-active-background);
    }

    &:last-child {
      margin-right: 0;
    }

    @include phones-small {
      &:last-child {
        margin-right: 0;
      }
    }
  }

  &__rates-description {
    font-style: normal;
    font-weight: normal;
    font-size: 16px;
    line-height: 24px;
    display: flex;
    justify-content: space-between;
    margin-bottom: 40px;
    padding: 0 50px;

    p {
      color: var(--zeen-survey-rates-descroption-color);
    }

    @include phones-small {
      width: 100%;
      padding: 0 30px;
    }
  }

  // multiple-rates

  &__multiple-rates-wrapper {
    padding: 40px 50px 30px;

    @include phones-small {
      padding: 40px 30px 30px;
    }
  }

  &__multiple-rates {
    display: flex;
    justify-content: space-between;
    width: 100%;
    margin-bottom: 10px;

    &:last-child {
      margin-bottom: 0;
    }

    @include phones-small {
      flex-direction: column;
    }
  }

  &__multiple-rates-title {
    @include phones-small {
      margin-bottom: 5px;
    }
  }

  &__multiple-rates-inner {
    display: flex;
    align-items: center;
    width: fit-content;
  }

  &__multiple-rate {
    display: flex;
    justify-content: center;
    align-items: center;
    width: var(--zeen-survey-rate-size);
    height: var(--zeen-survey-rate-size);
    background: var(--zeen-survey-rate-background);
    color: var(--zeen-survey-rate-color);
    border-radius: 50%;
    cursor: pointer;
    margin-right: var(--zeen-survey-rate-gap);

    &--active {
      color: var(--zeen-survey-rate-active-color);
      background: var(--zeen-survey-rate-active-background);
    }

    &:last-child {
      margin-right: 0;
    }

    @include phones-small {
      &:last-child {
        margin-right: 0;
      }
    }
  }
}
</style>