petra-tool / frontend / src / views / site / Settings.vue
Settings.vue
Raw
<template>
  <v-container class="settings_team">
    <app-bar title="Team Settings" :hide-drawer="true"></app-bar>

    <v-form ref="form" v-model="form.valid" lazy-validation>
      <v-container class="my-5">
        <v-card flat>
          <v-card-title class="text-h5">Set Cycle Duration
            <dialog-info>
              <template v-slot:title>Set Cycle Duration</template>
              <template v-slot:text>
                Cycles are time-bound development periods, ranging from 1 day to 3 weeks. 
                Teams focus on specific sub-phases for building prototypes to test hypotheses. 
                You can run more than one cycle according to your needs. 
                For shorter projects like hackathons, opt for one-day cycle, while longer projects benefit from 1 to 3-week durations. 
                Keep in mind that duration changes are only allowed when no active cycles are ongoing.
              </template>
            </dialog-info>
          </v-card-title>
          <v-card-text v-if="ongoingCyclesPresent">
                You can change the cycle duration only when there are no ongoing or reflective cycles.
          </v-card-text>
          <v-card-text v-else>
            <v-row no-gutters>
              <v-col cols="12">
                <v-slider
                  v-model="form.sprintSelected"
                  :tick-labels="form.sprintTicks"
                  :min="1"
                  :max="5"
                  step="1"
                  ticks="always"
                  tick-size="6"
                  class="pt-6 mx-1"
                  track-color="accent lighten-4"
                  color="accent"
                  :rules="[value => !!value || 'Value is required.']"
                ></v-slider>
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions v-if="!ongoingCyclesPresent">
            <v-spacer></v-spacer>
            <v-btn
              color="primary"
              depressed
              min-width="150"
              :loading="loading"
              @click="onSubmit"
            >
              Save
            </v-btn>
          </v-card-actions>
        </v-card>
      </v-container>

      <v-container class="my-5">
        <v-card flat>
          <v-card-text class="px-6">
            <v-row>
              <v-col cols="12" md="4">
                <div class="text-h5 text--primary">Member</div>
              </v-col>
              <v-col cols="12" md="3">
                <div class="text-h5 text--primary">Role
                  <dialog-info>
                    <template v-slot:title>Roles</template>
                    <template v-slot:text>
                      <ul>
                        <li><b>Project Manager (PM)</b> oversees workflow and ensures effective project management within the team.
                        </li>
                        <li><b>Problem Role (PR)</b> focuses on understanding user needs and ensuring product desirability.
                        </li>
                        <li><b>Tech Role (TR)</b> develops technically feasible solutions for the project.
                        </li>
                        <li><b>Business Role (BR)</b> analyzes the market and develops the project's business model.
                        </li>
                      </ul>
                    </template>
                  </dialog-info>
                </div>
              </v-col>
              <v-col cols="12" md="3">
                <div class="text-h5 text--primary">Working Hours Average
                  <dialog-info>
                    <template v-slot:title>Working Hours on Average</template>
                    <template v-slot:text>
                    Working hours reflect a member's time commitment to the project. 
                    A typical 40-hour week indicates full-time commitment, while 20 hours per week is half-time, and 10 hours per week is quarter-time. 
                    You have the flexibility to adjust your working hours during the project, provided there are no ongoing or reflective cycles.
                    </template>
                  </dialog-info>
                </div>
              </v-col>
              <v-col cols="12" md="2">
                <div class="text-h5 text--primary">Availability
                  <dialog-info>
                    <template v-slot:title>Availability</template>
                    <template v-slot:text>
                      The availability describes if a member is still working on the project or if he/she took a leave for some time (e.g., illness) or if the person left the
                      project. By activating the checkbox, the person is working on the project. By deactivating the checkbox, the person is on leave.
                    </template>
                  </dialog-info>
                </div>
              </v-col>
            </v-row>
            <v-form ref="formNewMember" v-model="form.validNewMember" lazy-validation>
              <v-row class="grey lighten-2 my-3">
                <v-col cols="12" md="4" class="pb-0 mb-0">
                  <v-text-field
                    label="Type Name"
                    color="secondary"
                    v-model="form.newMember.name"
                    outlined
                    maxlength="64"
                    :rules="[value => !!value || 'Name is required.',
                      value => form.members.filter(member => member.name === value).length === 0 || 'Name already exists.']"
                  ></v-text-field>
                </v-col>
                <v-col cols="12" md="3" class="pb-0 mb-0">
                  <v-select
                    label="Select Role"
                    color="secondary"
                    :items="form.roleOptions"
                    v-model="form.newMember.role"
                    item-text="text"
                    outlined
                    :rules="[value => !!value || 'Role is required.']"
                  ></v-select>
                </v-col>
                <v-col cols="12" md="3" class="pb-0 mb-0">
                  <v-select
                    label="Choose working hours..."
                    :items="form.hoursOptions"
                    v-model="form.newMember.hours"
                    return-object
                    outlined
                    :rules="[value => !!value || 'Value is required.']"
                  ></v-select>
                </v-col>
                <v-col cols="12" md="2" class="pb-0 mb-0">
                  <v-btn color="secondary"
                         depressed
                         class="mt-3"
                         @click="addMember">
                    Add Member
                  </v-btn>
                </v-col>
              </v-row>
            </v-form>
            <v-row v-for="member in form.members" :key="member.id">
              <v-col cols="12" md="4">
                <v-text-field
                  label="Name"
                  disabled
                  v-model="member.name"
                  outlined
                ></v-text-field>
              </v-col>
              <v-col cols="12" md="3">
                <v-text-field
                  label="Role"
                  disabled
                  v-model="member.role"
                  outlined
                ></v-text-field>
              </v-col>
              <v-col cols="12" md="3">
                <v-select
                  label="Working Hours"
                  :items="form.hoursOptions"
                  :disabled="!member.available"
                  v-model="member['hours']"
                  return-object
                  outlined
                  :rules="[value => !!value || 'Value is required.']"
                ></v-select>
              </v-col>
              <v-col cols="12" md="2">
                <v-checkbox v-model="member.available" inset color="secondary" :label="member.available ? 'available' : 'away'"/>
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions v-if="!ongoingCyclesPresent">
            <v-spacer></v-spacer>
            <v-btn
              color="primary"
              depressed
              min-width="150"
              :loading="loading"
              @click="onSubmit"
            >
              Save
            </v-btn>
          </v-card-actions>
        </v-card>
      </v-container>
    </v-form>
  </v-container>
</template>

<script>
import AppBar from "@/components/AppBar";
import DialogInfo from "@/components/DialogInfo";

export default {
  name: "Settings",
  components: {DialogInfo, AppBar},
  data: () => ({
    loading: false,
    form: {
      valid: true,
      validNewMember: true,
      members: [],
      hoursOptions: [10, 20, 40],
      sprintOptions: [
        {text: '0.2 Weeks (1 Day)', value: 0.2},
        {text: '0.6 Weeks (3 Days)', value: 0.6},
        {text: '1 Week (5 Days)', value: 1},
        {text: '2 Weeks (10 Days)', value: 2},
        {text: '3 Weeks (15 Days)', value: 3},
      ],
      sprintTicks: [
        '0.2 Weeks (1 Day)', '0.6 Weeks (3 Days)', '1 Week (5 Days)', '2 Weeks (10 Days)', '3 Weeks (15 Days)'
      ],
      sprintDict: {
        1: 0.2,
        2: 0.6,
        3: 1,
        4: 2,
        5: 3,
      },
      sprintSelected: null,
      roleOptions: [
        {text: 'Project Manager (PM)', value: 'PM'},
        {text: 'Problem Role (PR)', value: 'PR'},
        {text: 'Tech Role (TR)', value: 'TR'},
        {text: 'Business Role (BR)', value: 'BR'}
      ],
      newMember: {
        name: '',
        role: '',
        available: true,
        hours: 40,
      }
    }
  }),
  methods: {
    addMember() {
      if (this.$refs.formNewMember.validate()) {
        this.form.members.push(this.form.newMember)
        this.$refs.formNewMember.resetValidation()
        this.form.newMember = {name: '', role: '', available: true, hours: 40}
      }
    },
    onSubmit() {
      if (this.$refs.form.validate()) {
        this.loading = true
        this.$http.post('/team/update', {
          members: this.form.members,
          sprint: this.sprint
        }).then(() => {
          // fetch new user data and update store
          this.$store.dispatch('auth/fetch').then(response => {
            this.$store.dispatch('site/setTeam', response.data.data)
            this.updateValues()
          })
          this.$store.dispatch('site/fetchPrototyping')
          this.$snackbar.showMessage({
            content: 'Settings saved!'
          })
        })
          .catch(() => {
            this.$snackbar.showMessage({
              content: 'Something went wrong, please try again!', color: 'warning'
            })
          }).finally(() => (this.loading = false))
      }
    },
    updateValues() {
      this.form.members = this.$store.getters['site/getMembers']

      // https://stackoverflow.com/a/28191966
      this.form.sprintSelected = Object.keys(this.form.sprintDict).find(key => this.form.sprintDict[key] === this.$store.getters['site/getTeam'].sprint)
    }
  },
  computed: {
    ongoingCyclesPresent() {
      return this.$store.getters['site/getCycles'].some(cycle => cycle.status === 'Ongoing' || cycle.status === 'Reflect')
    },
    sprint() {
      return this.form.sprintDict[this.form.sprintSelected]
    }
  },
  mounted() {
    this.updateValues()
  }
}
</script>

<style scoped>

</style>