Source: management-system/src/frontend/views/EnvironmentProfile.vue

<template>
  <div>
    <v-toolbar>
      <v-toolbar-title>
        {{ `Environment Profile${environmentID ? ` (ID: ${environmentID})` : ''}` }}
      </v-toolbar-title>
      <v-spacer />
    </v-toolbar>
    <popup style="left: 37vw" :popupData="popupData" />
    <confirmation
      :title="'leave the environment profile page?'"
      :text="'You have unsaved changes.'"
      :show="showUnsavedOnCloseDialog"
      maxWidth="335px"
      @cancel="
        showUnsavedOnCloseDialog = false;
        confirm = false;
      "
      @continue="
        confirm = true;
        next();
      "
    />
    <v-container fluid>
      <v-row justify="center" id="wrapper">
        <v-col class="text-center centered">
          <v-card>
            <v-card-title></v-card-title>
            <v-form class="profileForm" @submit.prevent>
              <v-row class="mb-n4 mt-2 v-label theme--light">
                Choose one of the following profiles or make your own.
              </v-row>
              <v-radio-group v-model="chosenProfileName" :mandatory="false">
                <div v-for="profile in customProfiles" :key="profile.id" style="display: flex">
                  <v-radio :value="profile.name" v-on:change="allSaved = false">
                    <template v-slot:label>
                      <div>
                        <h4>{{ profile.name }}</h4>
                      </div>
                    </template>
                  </v-radio>
                  <div>
                    <v-icon color="primary" @click="editProfile(profile)">mdi-pencil</v-icon>
                    <v-icon
                      v-if="!premadeProfiles.includes(profile)"
                      @click="deleteProfile(profile)"
                      color="error"
                    >
                      mdi-delete
                    </v-icon>
                  </div>
                </div>
              </v-radio-group>
              <div class="addButtonWrapper">
                <v-btn small class="addButton" color="success" @click="openNewProfile()">
                  <v-icon left>mdi-plus</v-icon> Add Profile
                </v-btn>
                <span class="errorSpan" v-if="tooManyProfiles">
                  You cannot add more than 8 custom profiles.
                </span>
              </div>
            </v-form>
            <v-card-actions>
              <v-spacer />
              <v-btn color="error" :disabled="!chosenProfileName" @click="chosenProfileName = null">
                Reset Choice
              </v-btn>
              <v-btn color="primary" @click="saveProfileToServer()">Save</v-btn>
            </v-card-actions>
          </v-card>
          <ProfileEditor
            :show="showConstraintModal"
            :title="'Create a Custom Environment Profile'"
            :level="'environment'"
            @cancel="showConstraintModal = false"
          />
          <ProfileEditor
            :show="showEditingConstraintModal"
            :title="'Edit Environment Profile'"
            :level="'environment'"
            @cancel="showEditingConstraintModal = false"
            :isEditing="true"
            :envProfile="profileToBeEdited"
            :profileType="typeOfProfileToBeEdited"
          />
          <ProfilesCard @delete="deleteProfile" @add="addCustomProfile" @edit="editProfile" />
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script>
import axios from 'axios';
import { mapState } from 'vuex';
import { environmentPerformance as performance } from '@/frontend/assets/constraintProfiles.js';
import AlertWindow from '@/frontend/components/universal/Alert.vue';
import ProfileEditor from '@/frontend/components/profiles/ProfileEditor.vue';
import ProfilesCard from '@/frontend/components/profiles/ProfilesCard.vue';
import confirmation from '@/frontend/components/universal/Confirmation.vue';

/**
 * @module views
 */
/**
 * @memberof module:views
 * @module Vue:EnvironmentProfile
 *
 * @vue-prop {string} environmentID - id of the environment
 */
export default {
  name: 'EnvironmentProfile',
  props: {
    environmentID: { type: String },
  },
  components: {
    popup: AlertWindow,
    ProfileEditor,
    ProfilesCard,
    confirmation,
  },
  data: () => ({
    /** */
    customProfiles: [],
    /** */
    chosenProfileName: null,
    /** */
    showConstraintModal: false,
    /** */
    showEditingConstraintModal: false,
    /** */
    showUnsavedOnCloseDialog: false,
    /** */
    editingProfile: null,
    /** */
    profileToBeEdited: {},
    /** */
    typeOfProfileToBeEdited: '',
    /** */
    tooManyProfiles: false,
    /** */
    otherEnvironments: {
      headers: [
        { text: '', value: 'data-table-expand' },
        { text: 'Name', value: 'name', align: 'center' },
        { text: 'ID', value: 'id', align: 'center' },
        { text: 'Type', value: 'type', align: 'center' },
        { text: 'Actions', value: 'action', align: 'center' },
      ],
      environments: [],
    },
    popupData: {
      body: '',
      display: 'none',
      color: 'error',
    },
    confirm: false,
    allSaved: true,
    next: null,
  }),
  computed: mapState({
    premadeProfiles() {
      return [performance];
    },
    profiles() {
      return this.$store.getters['environmentStore/environmentProfiles'];
    },
    chosenProfile() {
      return this.profiles.find((p) => p.name === this.chosenProfileName);
    },
  }),
  mounted() {
    this.getSelectedEnvironmentProfile();
  },
  beforeRouteLeave(to, from, next) {
    if (!this.allSaved) {
      this.showUnsavedOnCloseDialog = true;
      this.next = next;
      if (this.confirm) {
        next();
      } else {
        next(false);
      }
    } else {
      next();
    }
  },
  methods: {
    /** */
    async editProfile(profile) {
      const profileString = await this.$store.getters['environmentStore/profileJSONById'](
        profile.id
      );
      this.profileToBeEdited = profileString;
      this.typeOfProfileToBeEdited = profile.type;

      this.showEditingConstraintModal = true;
    },
    /** */
    openConstraintModal(profileIndex) {
      if (profileIndex) this.chosenProfileName = this.profiles[profileIndex - 1].name;
      this.editingProfile = profileIndex;
      this.showConstraintModal = true;
    },
    /** */
    openNewProfile() {
      this.chosenProfileName = null;
      this.openConstraintModal();
    },
    /** */
    addCustomProfile(input) {
      const profile = { ...input };
      if (this.editingProfile && this.editingProfile > this.premadeProfiles.length) {
        this.profiles[this.editingProfile - 1] = profile;
      } else {
        if (this.customProfiles.length >= 8) {
          this.tooManyProfiles = true;
          return;
        }
        // modify profile name if it already exists
        profile.name = this.getUniqueProfileName(
          profile.name,
          this.profiles.map((p) => p.name)
        );
        this.customProfiles.push(profile);
      }

      this.editingProfile = null;
      this.chosenProfileName = profile.name;
      this.allSaved = false;
    },
    /** */
    deleteProfile(profile) {
      this.tooManyProfiles = false;
      // const customIndex = profileIndex - this.premadeProfiles.length;
      // this.customProfiles = this.customProfiles.filter((cp, i) => i !== customIndex);
      this.$store.dispatch('environmentStore/remove', { id: profile.id, type: profile.type });
      this.allSaved = false;
    },
    /** */
    getUniqueProfileName(name, existingNames) {
      if (existingNames.every((n) => n !== name)) return name;
      const newName = `${name}1`;
      if (existingNames.every((n) => n !== newName)) return newName;
      return this.getUniqueProfileName(newName, existingNames);
    },
    /** */
    getSelectedEnvironmentProfile() {
      if (!this.environmentID) {
        this.displayError('Could not get environment profile. No environment ID specified.');
        return;
      }

      axios
        .get(`/profile/environment/${this.environmentID}`)
        .then((result) => {
          if (!result || !result.data) {
            this.displayError('Could not get environment profile.');
          }
          this.addCustomProfile(result.data.profile);
        })
        .catch((e) => {
          this.displayError(e);
        });
    },
    /** */
    saveProfileToServer() {
      if (!this.environmentID) {
        this.displayError('Could not save environment profile. No ID environment specified.');
      }

      axios
        .put(`/profile/environment/${this.environmentID}`, this.chosenProfile)
        .then((result) => {
          if (!result || !result.data) this.displayError('Profile could not be saved.');
          this.allSaved = true;
        })
        .catch((e) => {
          this.$logger.debug(e);
        });
    },
    /** */
    displayError(message) {
      this.popupData.display = 'block';
      this.popupData.body = message;
    },
  },
};
</script>

<style lang="scss">
/* https://sass-lang.com/documentation/syntax#scss */

#secondCard {
  margin-top: 20px;
  padding-bottom: 10px;
}

.addButton {
  width: 140px;
  margin-left: -8px;
}

.profileForm {
  margin: -1.4rem 2.2rem 0;
}

.addButtonWrapper {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-top: -20px;
}
</style>