petra-tool / frontend / src / components / AddRequirementList.vue
AddRequirementList.vue
Raw
<template>
  <div class="requirement-list">
    <v-row no-gutters class="mb-5">
      <v-col cols="12">
        <div class="text-h5 text--primary">Requirement List</div>
        <div class="mb-3 body-2">
          Define which requirements are necessary for the product to meet the user requirements. A requirement is a slice of functionality that describes a product's
          appearance, components, and/or capabilities. Please do the following steps: (1) Add a requirement through the button, (2) Choose its importance from 100%, and (3)
          Assign purposes (Exploration, Evaluation, and/or Communication) that you need to assess during your experiments.
        </div>
      </v-col>
      <v-col cols="12">
        <v-btn depressed :block="$vuetify.breakpoint.smAndDown" min-width="150" color="primary" @click="addRequirement">Add Requirement</v-btn>
      </v-col>
    </v-row>
    <v-row no-gutters class="pb-2" v-if="requirements.length > 0">
      <v-col cols="5" class="accent--text body-2 font-weight-bold">Requirement Name</v-col>
      <v-col cols="2" class="accent--text body-2 font-weight-bold">Ranking</v-col>
      <v-col cols="1" class="accent--text body-2 font-weight-bold"></v-col>
      <v-col cols="4" class="accent--text body-2 font-weight-bold">
        <v-row v-if="$vuetify.breakpoint.mdAndUp" no-gutters>
          <v-col cols="4">Explore (Ex)</v-col>
          <v-col cols="4">Evaluate (Ev)</v-col>
          <v-col cols="4">Communicate (Com)</v-col>
        </v-row>
        <v-row v-else no-gutters>
          <v-col cols="4">(Ex)</v-col>
          <v-col cols="4">(Ev)</v-col>
          <v-col cols="4">(Com)</v-col>
        </v-row>
      </v-col>
    </v-row>
    <v-row v-for="(requirement, i) in requirements" :key="i" no-gutters>
      <v-col cols="5" class="pr-1">
        <v-text-field
          v-model="requirement.name"
          label="What is the Requirement Name?"
          outlined
          counter maxlength="60"
          :rules="[value => !!value || 'Name is required.']"
        ></v-text-field>
      </v-col>
      <v-col cols="2" class="px-1">
        <v-text-field
          v-model.number="requirement.rating"
          label="Ranking"
          outlined maxlength="3"
          @input="ratingChanged"
          type="number"
          :rules="[value => !!value || 'Ranking is required.',
          value => (value > 0 && value <= 100) || 'Ranking invalid.',
          value => ratingSum === 100 || 'Rankings must sum to 100.']"
        ></v-text-field>
      </v-col>
      <v-col cols="1" class="px-0">
        <v-btn
          icon
          color="gray"
          class="pt-4"
          @click="deleteRequirement(i)">
          <v-icon>mdi-delete</v-icon>
        </v-btn>
      </v-col>
      <v-col cols="4">
        <v-row no-gutters>
          <v-col cols="4">
            <v-checkbox v-model="requirement.purpose.exploration"
                        :label="$vuetify.breakpoint.smAndUp ? 'Ex' : ''"
                        :rules="[() => rules.atLeastOne(requirement.purpose)]"
            ></v-checkbox>
          </v-col>
          <v-col cols="4">
            <v-checkbox v-model="requirement.purpose.evaluation"
                        :label="$vuetify.breakpoint.smAndUp ? 'Ev' : ''"
                        :rules="[() => rules.atLeastOne(requirement.purpose)]"
                        hide-details
            ></v-checkbox>
          </v-col>
          <v-col cols="4">
            <v-checkbox v-model="requirement.purpose.communication"
                        :label="$vuetify.breakpoint.smAndUp ? 'Com' : ''"
                        :rules="[() => rules.atLeastOne(requirement.purpose)]"
                        hide-details
            ></v-checkbox>
          </v-col>
        </v-row>
      </v-col>
    </v-row>
    <v-row no-gutters v-if="requirements.length > 0">
      <v-col cols="12" class="body-2">
        Ranking left: {{ 100 - ratingSum }}/100
      </v-col>
    </v-row>
  </div>
</template>

<script>
import sumBy from 'lodash/sumBy'

export default {
  name: "AddRequirementList",
  props: ['requirements', 'cycleId', 'finish', 'requirementsDeleted'],
  data: () => ({
    name: '',
    rules: {
      atLeastOne: (requirement) => Object.values(requirement).some(x => !!x) || 'At least one required.'
    },
  }),
  methods: {
    addRequirement() {
      this.requirements.push({
        name: this.name,
        rating: null,
        purpose: {exploration: false, evaluation: false, communication: false},
        finish: this.finish,
      })
      this.name = ''
    },
    deleteRequirement(i) {
      this.requirementsDeleted.push(...this.requirements.splice(i, 1))
    },
    ratingChanged() {
      this.$emit('rating-changed')
    }
  },
  computed: {
    ratingSum() {
      return sumBy(this.requirements, 'rating')
    },

  },
  created: async function () {
    if (this.requirements.length === 0) { // one can toggle the checkbox and get copies of the same list
      await this.$http.get(`/board/label?cycle-id=${this.cycleId}`).then(res => {
        if (!res.data.finish) { // one can repeat requirement list planning and therefore return empty list
          let requirements = Object.values(res.data.requirements)
          requirements.forEach(requirement => {
            // Process requirements for checkboxes
            let purposes = {}
            requirement.purpose.forEach(purpose => {
              if (purpose.name === 'Explore') purposes.exploration = true;
              if (purpose.name === 'Evaluate') purposes.evaluation = true;
              if (purpose.name === 'Communicate') purposes.communication = true;
            })
            requirement.purpose = purposes
            requirement.finish = this.finish
            this.requirements.push(requirement)
          })
        } // otherwise nothing to finalize
      })
    }

  }
}
</script>

<style scoped>

</style>