<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> </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>