Skip to content
Snippets Groups Projects
  • moon's avatar
    201584bc
    #69 · 201584bc
    moon authored
    species_tax_id is removed.
    mandatory field are marked.
    Uniprot ID tooltip is added.
    Synthetic/Biomolecular for is_experimental field.
    201584bc
    History
    #69
    moon authored
    species_tax_id is removed.
    mandatory field are marked.
    Uniprot ID tooltip is added.
    Synthetic/Biomolecular for is_experimental field.
AddNovelCondensate.vue 24.85 KiB
<template>
  <div class="flex flex-wrap">
    <div class="w-5/6">
      <form
        autocomplete="off"
        @submit.prevent="addCondensate()"
      >
        <div v-if="isLoading">
          <base-spinner />
        </div>
        <div
          class="
            grid grid-cols-3 grid-flow-row-dense
            items-center
            gap-x-6
            border border-gray-300
            rounded-md
            p-4
            gap-y-4
          "
        >
          <label
            class="text-right"
            for="condensate_is_experimental"
          >Synthetic/Biomolecular</label>
          <div class="col-span-2">
            <input
              id="condensate_is_experimental_true"
              v-model="condensate.is_experimental"
              type="radio"
              name="condensate_is_experimental"
              value="true"
            >
            <label
              for="condensate_is_experimental_true"
              class="ml-2 mr-3"
            >Synthetic</label>
            <input
              id="condensate_is_experimental_false"
              v-model="condensate.is_experimental"
              type="radio"
              name="condensate_is_experimental"
              value="false"
            >
            <label
              for="condensate_is_experimental_false"
              class="ml-2 mr-3"
            >Biomolecular</label>
          </div>

          <label
            class="text-right"
            for="condensate_name"
          >Condensate Name</label>
          <div class="col-span-2">
            <input
              id="condensate_name"
              v-model.trim="condensate.name"
              class="
                bg-white
                w-1/3
                py-4
                px-4
                w-full
                rounded-lg
                text-gray-700
                bg-transparent
                border border-gray-500
                hover:border-gray-700
              "
              type="text"
              placeholder="Name"
            >

            <p
              v-if="condensate.errors.name"
              class="text-red-600 mt-4 font-bold"
            >
              {{ condensate.errorMsg }}
            </p>
          </div>

          <label
            class="text-right"
            for="condensate_protein"
          >Proteins</label>
          <div>
            <tags-input
              v-model="proteinNameList"
              @proteinNotFound="notFound"
            />
            <p
              v-if="searchProtein.errors"
              class="text-red-500 font-bold"
            >
              {{ searchProtein.message }}
            </p>
          </div>

          <button
            id="addNewProtein"
            class="
              bg-blue-500
              font-bold
              rounded-lg
              py-4
              px-5
              h-full
              text-white
              hover:bg-blue-700
            "
            type="button"
            @click="showAddNewProtein = !showAddNewProtein"
          >
            Add New Protein
          </button>

          <div
            v-if="showAddNewProtein"
            class="col-span-3"
          >
            <div
              class="
                grid grid-cols-3
                items-center
                grid-flow-row-dense
                gap-x-6 gap-y-4
                p-4
                border border-gray-300
                rounded-md
              "
            >
              <label
                class="text-right"
                for="uniprot_id"
              >Uniprot ID *
                <a
                  class="uniprot-link tooltipped tooltipped-e"
                  aria-label='The UniProt ID (accession) is the 6 to the 10-character-long name displayed on the heading of the protein page from Uniprot.
                  It is a unique identifier for a protein from the whole Uniprot database (https://www.uniprot.org/help/accession_numbers).
                  Please note that different proteins from the same gene will also have different Uniprot IDs.
                  For example, The Uniprot ID of "FUS Human" is P35637 (https://www.uniprot.org/uniprot/P35637).
                  It is usually the last part of the URI of the HTTPS link of the protein page.'
                >
                  <span class="fa fa-info-circle" />
                </a>
              </label>
              <div class="col-span-2">
                <input
                  id="uniprot_id"
                  v-model.trim="newProtein.uniprot_id"
                  class="
                    bg-white
                    w-1/3
                    py-4
                    px-4
                    w-full
                    rounded-lg
                    text-gray-700
                    bg-transparent
                    border border-gray-500
                    hover:border-gray-700
                  "
                  type="text"
                  placeholder="Uniprot ID"
                >

                <p
                  v-if="newProtein.errors.uniprot_id"
                  class="text-red-600 mt-4 font-bold"
                >
                  {{ newProtein.errorMsg }}
                </p>
              </div>

              <label
                class="text-right"
                for="functional_type"
              >Functional Type</label>
              <div class="col-span-2">
                <select
                  id="functional_type"
                  v-model="newProtein.functional_type"
                  class="
                    bg-white
                    px-4
                    py-5
                    rounded-lg
                    border border-gray-400
                    w-full
                    text-gray-700
                    outline-none
                    bg-transparent
                    border-b
                    hover:border-blue-700
                  "
                >
                  <option value="">
                    Select Functional Type
                  </option>
                  <option value="client">
                    client
                  </option>
                  <option value="driver">
                    driver
                  </option>
                  <option value="null">
                    unknown
                  </option>
                  <option value="regulator">
                    regulator
                  </option>
                </select>

                <p
                  v-if="newProtein.errors.functional_type"
                  class="text-red-600 mt-4 font-bold"
                >
                  {{ newProtein.errorMsg }}
                </p>
              </div>

              <label
                class="text-right"
                for="pubmed_ids"
              >PubMed IDs *</label>
              <div class="col-span-2">
                <input
                  id="pubmed_ids"
                  v-model.trim="newProtein.pubmed_ids"
                  class="
                    bg-white
                    w-1/3
                    py-4
                    px-4
                    w-full
                    rounded-lg
                    text-gray-700
                    bg-transparent
                    border border-gray-500
                    hover:border-gray-700
                  "
                  type="text"
                  placeholder="PubMed IDs (separated by comma)"
                >

                <p
                  v-if="newProtein.errors.pubmed_ids"
                  class="text-red-600 mt-4 font-bold"
                >
                  {{ newProtein.errorMsg }}
                </p>
              </div>

              <label
                class="text-right"
                for="driver_criterion"
              >Driver Criterion</label>
              <div class="col-span-2 space-x-4">
                <input
                  id="self_ps"
                  v-model="newProtein.driver_criterion"
                  type="checkbox"
                  value="self_ps"
                  class="h-6 w-6"
                >

                <label
                  for="self_ps"
                  class="mx-3"
                >Self-PS</label>

                <input
                  id="induces_assembly"
                  v-model="newProtein.driver_criterion"
                  type="checkbox"
                  value="induces_assembly"
                  class="h-6 w-6"
                >

                <label
                  for="induces_assembly"
                  class="mx-3"
                >Induces Assembly</label>

                <input
                  id="essential_for_integrity"
                  v-model="newProtein.driver_criterion"
                  type="checkbox"
                  value="essential_for_integrity"
                  class="h-6 w-6"
                >
                <label
                  for="essential_for_integrity"
                  class="mx-3"
                >Essential For Integrity</label>
                <p
                  v-if="newProtein.errors.driver_criterion"
                  class="text-red-600 mt-4 font-bold"
                >
                  {{ newProtein.errorMsg }}
                </p>
              </div>

              <label
                class="text-right"
                for="experimental_evidences"
              >Experimental Evidences</label>
              <div class="col-span-2">
                <input
                  id="in_vitro"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="in_vitro"
                  class="h-6 w-6"
                >
                <label
                  for="in_vitro"
                  class="mx-3"
                >In Vitro</label>
                <input
                  id="in_vivo"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="in_vivo"
                  class="h-6 w-6"
                >
                <label
                  for="in_vivo"
                  class="mx-3"
                >In Vivo</label>
                <input
                  id="in_cellulo"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="in_cellulo"
                  class="h-6 w-6"
                >
                <label
                  for="in_cellulo"
                  class="mx-3"
                >In Cellulo</label>
                <input
                  id="mass_spectrometry"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="mass_spectrometry"
                  class="h-6 w-6"
                >
                <label
                  for="mass_spectrometry"
                  class="mx-3"
                >Mass Spectrometry</label><br>
                <input
                  id="colocalization"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="colocalization"
                  class="h-6 w-6"
                >
                <label
                  for="colocalization"
                  class="mx-3"
                >Colocalization</label>
                <input
                  id="frap"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="frap"
                  class="h-6 w-6"
                >
                <label
                  for="frap"
                  class="mx-3"
                >FRAP</label>
                <input
                  id="others"
                  v-model="newProtein.experimental_evidences"
                  type="checkbox"
                  value="others"
                  class="h-6 w-6"
                >
                <label
                  for="others"
                  class="mx-3"
                >Others</label>
                <p
                  v-if="newProtein.errors.experimental_evidences"
                  class="text-red-600 mt-4 font-bold"
                >
                  {{ newProtein.errorMsg }}
                </p>
              </div>

              <label>&nbsp;</label>
              <div class="col-span-2">
                <div class="space-y-4 space-x-4">
                  <button
                    id="addNewProteinButton"
                    class="
                      bg-blue-500
                      font-bold
                      rounded-lg
                      py-4
                      px-5
                      w-32
                      text-white
                      hover:bg-blue-700
                    "
                    type="button"
                    @click="addNewProtein()"
                  >
                    Add
                  </button>
                  <button
                    id="cancelNewProtinButton"
                    class="
                      bg-white
                      hover:bg-gray-200
                      focus:ring-2 focus:ring-gray-300
                      rounded-lg
                      border border-gray-300
                      py-4
                      px-5
                      hover:text-gray-900
                      font-bold
                    "
                    type="button"
                    @click="cancelAddProtein"
                  >
                    Cancel
                  </button>
                </div>
              </div>
            </div>
          </div>

          <label
            class="text-right"
            for="condensate_synonyms"
          >Synonyms</label>
          <div class="col-span-2">
            <input
              id="condensate_synonyms"
              v-model.trim="condensate.synonyms"
              class="
                bg-white
                w-1/3
                py-4
                px-4
                w-full
                rounded-lg
                text-gray-700
                bg-transparent
                border border-gray-500
                hover:border-gray-700
              "
              type="text"
              placeholder="Synonyms (comma-separated)"
            >

            <p
              v-if="condensate.errors.synonyms"
              class="text-red-600 mt-4 font-bold"
            >
              {{ condensate.errorMsg }}
            </p>
          </div>

          <label
            class="text-right"
            for="condensate_comments"
          >Comments</label>
          <div class="col-span-2">
            <textarea
              id="condensate_comments"
              v-model.trim="condensate.comments"
              class="
                text-xl
                outline-none
                w-full
                bg-transparent
                px-4
                py-5
                rounded-lg
                border border-gray-400
                hover:border-gray-700
                focus:border-blue-500
              "
              rows="5"
              :placeholder="
                getUserData === 'Maintainer' ? 'Optional.' : 'Mandatory.'
              "
            />
            <p
              v-if="condensate.errors.comments"
              class="text-red-600 mt-4 font-bold"
            >
              {{ condensate.errorMsg }}
            </p>
          </div>

          <div class="col-start-2 col-end-3">
            <div class="space-y-4 space-x-4">
              <button
                class="
                  bg-blue-500
                  font-bold
                  rounded-lg
                  py-6
                  px-5
                  text-white
                  hover:bg-blue-700
                "
                type="submit"
              >
                Add Condensate
              </button>
              <button
                id="dropdownMenuButton"
                class="
                  bg-white
                  hover:bg-gray-200
                  focus:ring-2 focus:ring-gray-300
                  rounded-lg
                  border border-gray-300
                  py-6
                  px-5
                  hover:text-gray-900
                  font-bold
                "
                type="button"
                @click="close"
              >
                Cancel
              </button>
            </div>
          </div>
        </div>

        <div>
          <h3
            v-if="fetchError"
            class="text-red-500"
          >
            {{ fetchError }}
          </h3>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
// require modules
/* eslint-disable no-unused-vars */
import TagsInput from '@/components/TagsInput.vue';
import BaseSpinner from './UI/BaseSpinner.vue';
import { apikey } from '@/components/js/const';
const _ = require('lodash');

export default {
  name: 'AddNovelCondensate',
  components: {
    TagsInput,
    BaseSpinner,
  },
  props: [],
  data() {
    return {
      showAddNewProtein: false,
      searchProtein: {
        errors: false,
        message: '',
      },
      newProtein: {
        uniprot_id: '',
        functional_type: '',
        pubmed_ids: '',
        driver_criterion: [],
        experimental_evidences: [],
        errors: {
          uniprot_id: false,
          functional_type: false,
          pubmed_ids: false,
          driver_criterion: false,
          experimental_evidences: false,
        },
        errorMsg: '',
      },
      condensate: {
        name: '',
        is_experimental: false,
        proteins: [],
        synonyms: '',
        comments: '',
        errors: {
          name: false,
          species: false,
          synonyms: false,
          comments: false,
        },
        errorMsg: '',
      },
      isLoading: false,
      proteinList: this.$store.getters['Param/proteinList'],
      fetchError: '',
      isDev: process.env.NODE_ENV === 'development',
    };
  },
  computed: {
    jwt: function () {
      return this.$store.getters['User/jwt'];
    },
    getUserData() {
      const userRole = this.$store.getters['User/userRole'];
      // console.log('role is', userRole);
      return userRole;
    },
    proteinNameList: {
      get() {
        return _.map(this.proteinList, (d) => {
          return { text: d.uniprot_id };
        });
      },
      set(obj) {
        const idx = this.proteinList.findIndex(
          (d) => d.uniprot_id === obj.tag.text
        );
        this.$store.dispatch('Param/removeProtein', idx);
        obj.deleteTag();
        // console.log(this.$store.getters['Param/proteinList'])
      },
    },
  },
  mounted: function () {
    const vm = this;
    // vm.proteinNameList.push(vm.protein);
    vm.$store.dispatch('Param/resetProteins');
  },
  methods: {
    notFound(val, isEmpty) {
      if (!val && !isEmpty) {
        this.searchProtein.errors = true;
        this.searchProtein.message =
          'Protein not found. Try adding new protein.';
      } else {
        this.searchProtein.errors = false;
        this.searchProtein.message = '';
      }
    },
    close() {
      this.condensate.name = '';
      this.condensate.is_experimental = false;
      this.condensate.species = '';
      this.condensate.proteins = [];
      this.condensate.synonyms = '';
      this.condensate.comments = '';
      this.condensate.errorMsg = '';
      this.$router.push('/');
    },
    cancelAddProtein() {
      this.newProtein.uniprot_id = '';
      this.newProtein.functional_type = '';
      this.newProtein.pubmed_ids = '';
      this.newProtein.driver_criterion = [];
      this.newProtein.experimental_evidences = [];
      this.newProtein.errorMsg = '';
      this.showAddNewProtein = false;
    },

    async addNewProtein() {
      const vm = this;

      let host = vm.isDev
        ? require('./js/const').devHost
        : require('./js/const').host;

      Object.keys(vm.newProtein.errors).forEach(
        (v) => (vm.newProtein.errors[v] = false)
      );

      if (vm.newProtein.uniprot_id === '') {
        vm.newProtein.errorMsg = 'Uniprot ID should not be empty!';
        vm.newProtein.errors.uniprot_id = true;
        return;
      } else if (vm.newProtein.uniprot_id.length < 6) {
        vm.newProtein.errorMsg = 'Uniprot ID should be minimum of 6 character.';
        vm.newProtein.errors.uniprot_id = true;
        return;
      } else if (vm.newProtein.uniprot_id.length > 10) {
        vm.newProtein.errorMsg =
          'Uniprot ID should be maximum of 10 character.';
        vm.newProtein.errors.uniprot_id = true;
        return;
      } else if (/(\s)/.test(vm.newProtein.uniprot_id)) {
        vm.newProtein.errorMsg = 'Uniprot ID should not have space in between.';
        vm.newProtein.errors.uniprot_id = true;
        return;
      } else {
        let host = vm.isDev
          ? require('./js/const').devHost
          : require('./js/const').host;

        let url = `${host}/proteins?fields=uniprot_id,gene_name,name&query=${vm.newProtein.uniprot_id}`;

        const ret = await fetch(url, {
          method: 'GET',
          mode: 'cors',
          cache: 'no-cache',
          headers: {
            Authorization: `Bearer ${apikey}`,
          },
        }).then((response) => response.json());

        if (ret.count !== 0) {
          vm.newProtein.errorMsg =
            'Uniprot ID already exists in the database. Add the tag!';
          vm.newProtein.errors.uniprot_id = true;
          return;
        }
      }

      if (vm.newProtein.pubmed_ids === '') {
        vm.newProtein.errorMsg = 'Pubmed IDs should not be empty!';
        vm.newProtein.errors.pubmed_ids = true;
        return;
      }

      // if (vm.newProtein.driver_criterion === '') {
      //   vm.newProtein.errorMsg = 'Driver criterion should not be empty!';
      //   vm.newProtein.errors.driver_criterion = true;
      //   return;
      // }
      //
      // if (vm.newProtein.experimental_evidences === '') {
      //   vm.newProtein.errorMsg = 'Experimental evidences should not be empty!';
      //   vm.newProtein.errors.experimental_evidences = true;
      //   return;
      // }

      vm.$store.dispatch('Param/addProtein', {
        uniprot_id: vm.newProtein.uniprot_id,
        functional_type: vm.newProtein.functional_type,
        proteins: vm.newProtein.proteins,
        pubmed_ids: vm.newProtein.pubmed_ids,
        driver_criterion: vm.newProtein.driver_criterion,
        experimental_evidences: vm.newProtein.experimental_evidences,
      });

      vm.newProtein.uniprot_id =
        vm.newProtein.functional_type =
        vm.newProtein.pubmed_ids = '';
      vm.newProtein.driver_criterion = [];
      vm.newProtein.experimental_evidences = [];
      vm.showAddNewProtein = false;
    },
    async addCondensate() {
      const vm = this;

      Object.keys(vm.condensate.errors).forEach(
        (v) => (vm.condensate.errors[v] = false)
      );

      if (vm.condensate.name === '') {
        vm.condensate.errorMsg = 'Condensate name should not be empty!';
        vm.condensate.errors.name = true;
        return;
      } else if (vm.proteinList.length === 0) {
        vm.searchProtein.errors = true;
        vm.searchProtein.message = 'Proteins field should not be empty.';
        return;
      }

      let host = vm.isDev
        ? require('./js/const').devApiHost
        : require('./js/const').apiHost;

      let url = `${host}/api/novel-condensates`;
      let data;
      if (this.getUserData === 'Maintainer') {
        data = {
          Name: vm.condensate.name,
          IsExperimental: vm.condensate.is_experimental,
          Proteins: vm.proteinList,
          Synonyms: vm.condensate.synonyms,
          Status: 'accepted',
          SubmissionComments:
            'Maintainer do not need to provide a reason for such change at the moment!',
        };
      } else {
        data = {
          Name: vm.condensate.name,
          IsExperimental: vm.condensate.is_experimental,
          Proteins: vm.proteinList,
          Synonyms: vm.condensate.synonyms,
          Status: 'requested',
          SubmissionComments: vm.condensate.comments,
        };
      }
      vm.isLoading = true;
      try {
        await vm.axios.post(
          url,
          { data: data },
          {
            headers: {
              Authorization: `Bearer ${vm.jwt}`,
            },
          }
        );

        vm.isLoading = false;
        vm.condensate.is_experimental = false;
        vm.searchProtein.errors = false;
        vm.condensate.name =
          vm.condensate.species =
          vm.condensate.synonyms =
          vm.condensate.comments =
            '';
        vm.$store.dispatch('Param/resetProteins');
        vm.$emit('showToaster', 'Condensate added successfully!');

        setTimeout(() => {
          vm.$emit('hideToaster');
        }, 2000);
      } catch (e) {
        console.error(e);
        vm.isLoading = false;
        vm.fetchError =
          e.message || 'Something went wrong, please try again later!';
        vm.isSubmitted = true;
        vm.error = true;
      }
    },
  },
};
</script>

<style scoped>
@import url("~@/assets/bootstrap.css");
@import url("~@/assets/datatable.css");
@import url("~@/assets/tooltip.css");
.mainContent {
  padding: 5px;
}

.uniprot-link {
  font-weight: bold;
  color: #ef0087 !important;
}

</style>