From 05184414860a7e3b055a059c2b29da51a0ef3454 Mon Sep 17 00:00:00 2001 From: HongKee Moon <moon@mpi-cbg.de> Date: Thu, 12 May 2022 16:32:44 +0200 Subject: [PATCH] dd-code-team/dd-code-web#69: UI for data input and post to the server --- web/src/components/AddNovelCondensate.vue | 657 ++++++++++++++++++++++ web/src/components/LandingPage.vue | 4 +- web/src/components/Links.vue | 9 + web/src/components/ProteinDetailPage.vue | 299 +--------- web/src/components/TagsInput.vue | 94 ++++ web/src/router/index.js | 5 + web/src/store/modules/Param.js | 46 ++ 7 files changed, 824 insertions(+), 290 deletions(-) create mode 100644 web/src/components/AddNovelCondensate.vue create mode 100644 web/src/components/TagsInput.vue create mode 100644 web/src/store/modules/Param.js diff --git a/web/src/components/AddNovelCondensate.vue b/web/src/components/AddNovelCondensate.vue new file mode 100644 index 0000000..b324c2e --- /dev/null +++ b/web/src/components/AddNovelCondensate.vue @@ -0,0 +1,657 @@ +<template> + <div class="mainContent"> + <div class="panel-body"> + <div class="container-fluid col-md-12"> + <form + class="form-horizontal" + autocomplete="off" + @submit.prevent="addCondensate()" + > + <div v-if="isLoading"> + <base-spinner /> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_is_experimental" + >Synthetic?</label> + <div class="col-sm-4 pt-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" + >Yes</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" + >No</label> + </div> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_name" + >Condensate Name</label> + <div class="col-sm-4"> + <input + id="condensate_name" + v-model.trim="condensate.name" + class="form-control input-sm" + type="text" + placeholder="Name" + > + </div> + <p + v-if="condensate.errors.name" + class="text-red-600 mt-4 font-bold" + > + {{ condensate.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_species" + >Species</label> + <div class="col-sm-4"> + <input + id="condensate_species" + v-model.trim="condensate.species" + class="form-control input-sm" + type="text" + placeholder="Species (NCBI taxonomy ID)" + > + </div> + <p + v-if="condensate.errors.species" + class="text-red-600 mt-4 font-bold" + > + {{ condensate.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_protein" + >Proteins</label> + <div class="col-sm-4"> + <tags-input v-model="proteinNameList" /> + </div> + <div class="col-sm-2"> + <button + id="addNewProtein" + class=" + text-white + bg-indigo-500 + hover:bg-indigo-700 + focus:ring-2 focus:ring-blue-300 + rounded-lg + inline-flex + items-center + px-3 + py-2 + text-center + font-bold + " + type="button" + @click="showAddNewProtein = !showAddNewProtein" + > + Add New Protein + </button> + </div> + </div> + + <div + v-if="showAddNewProtein" + class="panel panel-default mt-4" + > + <div class="panel-body"> + <div class="container-fluid"> + <div v-if="isLoading"> + <base-spinner /> + </div> + + <div class="form-group"> + <label + class="control-label col-sm-2" + for="uniprot_id" + >Uniprot ID</label> + <div class="col-sm-4"> + <input + id="uniprot_id" + v-model.trim="newProtein.uniprot_id" + class="form-control input-sm" + type="text" + placeholder="Uniprot ID" + > + </div> + <p + v-if="newProtein.errors.uniprot_id" + class="text-red-600 mt-4 font-bold" + > + {{ newProtein.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="functional_type" + >Functional Type</label> + <div class="col-sm-4"> + + <select id="functional_type" + class="form-control input-sm" + v-model="newProtein.functional_type"> + <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> + </div> + <p + v-if="newProtein.errors.functional_type" + class="text-red-600 mt-4 font-bold" + > + {{ newProtein.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="pubmed_ids" + >PubMed IDs</label> + <div class="col-sm-4"> + <input + id="pubmed_ids" + v-model.trim="newProtein.pubmed_ids" + class="form-control input-sm" + type="text" + placeholder="PubMed IDs (separated by comma)" + > + </div> + <p + v-if="newProtein.errors.pubmed_ids" + class="text-red-600 mt-4 font-bold" + > + {{ newProtein.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="driver_criterion" + >Driver Criterion</label> + <div class="col-sm-4"> + <input + id="driver_criterion" + v-model.trim="newProtein.driver_criterion" + class="form-control input-sm" + type="text" + placeholder="Driver Criterion" + > + </div> + <p + v-if="newProtein.errors.driver_criterion" + class="text-red-600 mt-4 font-bold" + > + {{ newProtein.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="experimental_evidences" + >Experimental Evidences</label> + <div class="col-sm-4"> + <input + id="experimental_evidences" + v-model.trim="newProtein.experimental_evidences" + class="form-control input-sm" + type="text" + placeholder="Experimental Evidences" + > + </div> + <p + v-if="newProtein.errors.experimental_evidences" + class="text-red-600 mt-4 font-bold" + > + {{ newProtein.errorMsg }} + </p> + + <div class="flex space-x-4"> + <button + id="addNewProteinButton" + class=" + text-white + bg-indigo-500 + hover:bg-indigo-700 + focus:ring-2 focus:ring-blue-300 + rounded-lg + inline-flex + items-center + px-5 + py-2.5 + text-center + font-bold + " + 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 + px-5 + py-2.5 + hover:text-gray-900 + font-bold + " + type="button" + @click="showAddNewProtein = !showAddNewProtein" + > + Cancel + </button> + </div> + </div> + </div> + </div> + </div> + + + + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_synonyms" + >Synonyms</label> + <div class="col-sm-4"> + <input + id="condensate_synonyms" + v-model.trim="condensate.synonyms" + class="form-control input-sm" + type="text" + placeholder="Synonyms (comma-separated)" + > + </div> + <p + v-if="condensate.errors.synonyms" + class="text-red-600 mt-4 font-bold" + > + {{ condensate.errorMsg }} + </p> + </div> + <div class="form-group"> + <label + class="control-label col-sm-2" + for="condensate_comments" + >Comments</label> + <div class="col-sm-4"> + <textarea + id="condensate_comments" + v-model.trim="condensate.comments" + class=" + form-control + block + px-3 + py-1.5 + text-base + font-normal + text-gray-700 + bg-white bg-clip-padding + border border-solid border-gray-300 + rounded + transition + ease-in-out + m-0 + focus:text-gray-700 + focus:bg-white + focus:border-blue-600 + focus:outline-none + " + 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 class="flex space-x-4"> + <button + id="dropdownMenuButton" + class=" + text-white + bg-blue-500 + hover:bg-blue-700 + focus:ring-2 focus:ring-blue-300 + rounded-lg + inline-flex + items-center + px-5 + py-2.5 + text-center + font-bold + " + type="submit" + > + Add + </button> + <button + id="dropdownMenuButton" + class=" + bg-white + hover:bg-gray-200 + focus:ring-2 focus:ring-gray-300 + rounded-lg + border border-gray-300 + px-5 + py-2.5 + hover:text-gray-900 + font-bold + " + type="button" + @click="$emit('hide')" + > + Cancel + </button> + </div> + </div> + </div> + <div> + <h3 + v-if="fetchError" + class="text-red-500" + > + {{ fetchError }} + </h3> + </div> + </form> + </div> + </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: ['proteinId', 'hide'], + data() { + return { + showAddNewProtein: false, + 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, + species: '', + proteins: [], + synonyms: [], + comments: '', + errors: { + name: false, + species: false, + synonyms: false, + comments: false, + }, + errorMsg: '' + }, + isLoading: false, + proteinList: this.$store.getters['Param/proteinList'], + protein: this.$route.params.protein + ? this.$route.params.protein + : this.proteinId, + 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/addProtein', { uniprot_id: vm.protein }); + }, + methods: { + 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.functional_type === '') { + vm.newProtein.errorMsg = 'Functional type should not be empty!'; + vm.newProtein.errors.functional_type = 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.condensate.species === '') { + vm.condensate.errorMsg = 'Species should not be empty!'; + vm.condensate.errors.species = true; + return; + } else if (!/^\d+$/.test(vm.condensate.species)) { + vm.condensate.errorMsg = 'Species should be numbers!'; + vm.condensate.errors.species = true; + return; + } else if (vm.condensate.synonyms === '') { + vm.condensate.errorMsg = + 'Synonyms should not be empty! (Separate with comma)'; + vm.condensate.errors.synonyms = true; + 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, + SpeciesTaxId: vm.condensate.species, + Proteins: vm.proteinList, + Synonyms: vm.condensate.synonyms, + 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, + SpeciesTaxId: vm.condensate.species, + Proteins: vm.proteinList, + Synonyms: vm.condensate.synonyms, + SubmissionComments: vm.condensate.comments, + }; + } + vm.isLoading = true; + try { + await vm.axios.post( + url, + { data: data }, + { + headers: { + Authorization: `Bearer ${vm.jwt}`, + }, + } + ); + + vm.isLoading = false; + vm.toasterIsOpen = true; + vm.toasterMessage = 'Condensate added successfully!'; + vm.condensate.is_experimental = false; + vm.condensate.name = vm.condensate.species = vm.condensate.synonyms = vm.condensate.comments = ''; + vm.$emit('hide') + + setTimeout(() => { + vm.toasterIsOpen = false; + }, 2000); + } catch (e) { + console.error(e); + 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'); + +.mainContent { + padding: 20px; +} +</style> \ No newline at end of file diff --git a/web/src/components/LandingPage.vue b/web/src/components/LandingPage.vue index 7a13d55..ca0b50d 100644 --- a/web/src/components/LandingPage.vue +++ b/web/src/components/LandingPage.vue @@ -220,12 +220,12 @@ export default { proteinNameList: { get() { return _.map(this.proteinList, (d) => { - return { text: d.proteinName }; + return { text: d.uniprot_id }; }); }, set(obj) { const idx = this.proteinList.findIndex( - (d) => d.proteinName === obj.tag.text + (d) => d.uniprot_id === obj.tag.text ); this.$store.dispatch('Param/removeProtein', idx); obj.deleteTag(); diff --git a/web/src/components/Links.vue b/web/src/components/Links.vue index 8b0f8ad..a6ef11c 100644 --- a/web/src/components/Links.vue +++ b/web/src/components/Links.vue @@ -247,6 +247,15 @@ <span class="text-2xl">Help</span> </router-link> </li> +<!-- <li--> +<!-- role="presentation"--> +<!-- :class="{ active: $route.name === 'test' }"--> +<!-- @mouseover="openCondensateSubMenu = false"--> +<!-- >--> +<!-- <router-link to="/test/P62750">--> +<!-- <span class="text-2xl">Test</span>--> +<!-- </router-link>--> +<!-- </li>--> <li v-show="userData !== null" role="presentation" diff --git a/web/src/components/ProteinDetailPage.vue b/web/src/components/ProteinDetailPage.vue index 52d26ca..b2a4ee2 100644 --- a/web/src/components/ProteinDetailPage.vue +++ b/web/src/components/ProteinDetailPage.vue @@ -419,177 +419,7 @@ v-if="showAddCondensate" class="panel panel-default mt-4" > - <div class="panel-body"> - <div class="container-fluid col-md-12"> - <form - class="form-horizontal" - autocomplete="off" - @submit.prevent="addCondensate(response)" - > - <div v-if="isLoading"> - <base-spinner /> - </div> - <div class="form-group"> - <label - class="control-label col-sm-2" - for="condensate_is_experimental" - >Synthetic?</label> - <div class="col-sm-4 pt-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" - >Yes</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" - >No</label> - </div> - </div> - <div class="form-group"> - <label - class="control-label col-sm-2" - for="condensate_name" - >Condensate Name</label> - <div class="col-sm-4"> - <input - id="condensate_name" - v-model.trim="condensate.name" - class="form-control input-sm" - type="text" - placeholder="Name" - > - </div> - <p - v-if="isUniProtIdError" - class="text-red-600 mt-4 font-bold" - > - {{ uniprotIdErrorMsg }} - </p> - </div> - <div class="form-group"> - <label - class="control-label col-sm-2" - for="condensate_species" - >Species</label> - <div class="col-sm-4"> - <input - id="condensate_species" - v-model.trim="condensate.species" - class="form-control input-sm" - type="text" - placeholder="Species (NCBI taxonomy ID)" - > - </div> - </div> - <div class="form-group"> - <label - class="control-label col-sm-2" - for="condensate_synonyms" - >Synonyms</label> - <div class="col-sm-4"> - <input - id="condensate_synonyms" - v-model.trim="condensate.synonyms" - class="form-control input-sm" - type="text" - placeholder="Synonyms (comma-separated)" - > - </div> - </div> - <div class="form-group"> - <label - class="control-label col-sm-2" - for="condensate_comments" - >Comments</label> - <div class="col-sm-8"> - <textarea - id="condensate_comments" - v-model.trim="condensate.comments" - class=" - form-control - block - px-3 - py-1.5 - text-base - font-normal - text-gray-700 - bg-white bg-clip-padding - border border-solid border-gray-300 - rounded - transition - ease-in-out - m-0 - focus:text-gray-700 - focus:bg-white - focus:border-blue-600 - focus:outline-none - " - rows="5" - :placeholder=" - getUserData === 'Maintainer' - ? 'Optional.' - : 'Mandatory.' - " - /> - - <div class="flex space-x-4"> - <button - id="dropdownMenuButton" - class=" - text-white - bg-blue-500 - hover:bg-blue-700 - focus:ring-2 focus:ring-blue-300 - rounded-lg - inline-flex - items-center - px-5 - py-2.5 - text-center - font-bold - " - type="submit" - > - Add - </button> - <button - id="dropdownMenuButton" - class=" - bg-white - hover:bg-gray-200 - focus:ring-2 focus:ring-gray-300 - rounded-lg - border border-gray-300 - px-5 - py-2.5 - hover:text-gray-900 - font-bold - " - type="button" - @click="showAddCondensate = !showAddCondensate" - > - Cancel - </button> - </div> - </div> - </div> - </form> - </div> - </div> + <add-novel-condensate :protein="response.data.uniprot_id" @hide="showAddCondensate = false"/> </div> <div class="panel panel-default"> <table class="csi table table-hover table-responsive"> @@ -655,6 +485,7 @@ import AddDeletePubmed from './CMS/addDeletePubmed.vue'; //import FetchUserSpecificUpdateItems from './CMS/fetchUserSpecificUpdateItems.vue'; import ProteinUpdateItemTable from './ProteinUpdateItemTable.vue'; import BaseSpinner from './UI/BaseSpinner.vue'; +import AddNovelCondensate from "@/components/AddNovelCondensate"; import {apiHost as host} from '@/components/js/const'; //import BaseToaster from "./UI/BaseToaster.vue"; @@ -673,6 +504,7 @@ export default { ProteinUpdateItemTable, BaseSpinner, + AddNovelCondensate }, props: ['proteinId'], @@ -684,8 +516,16 @@ export default { name: '', is_experimental: false, species: '', + protein: '', synonyms: [], comments: '', + errors: { + name: false, + species: false, + synonyms: false, + comments: false, + }, + errorMsg: '' }, showUpdateFunctionType: false, showAddOrDeletePubmedId: false, @@ -782,123 +622,6 @@ export default { }); } }, - async addCondensate(response) { - if (this.condensate.name === '') { - this.uniprotIdErrorMsg = 'Condensate name should not be empty!'; - this.isUniProtIdError = true; - return; - } else if (this.uniprotId.length < 6) { - this.uniprotIdErrorMsg = 'Uniprot ID should be minimum of 6 character.'; - this.isUniProtIdError = true; - return; - } else if (this.uniprotId.length > 10) { - this.uniprotIdErrorMsg = - 'Uniprot ID should be maximum of 10 character.'; - this.isUniProtIdError = true; - return; - } else if (this.whitespaceRegex.test(this.uniprotId)) { - this.uniprotIdErrorMsg = 'Uniprot ID should not have space in between.'; - this.isUniProtIdError = true; - return; - } - - const findUniprotId = response.data.proteins.find( - (u) => u.uniprot_id === this.uniprotId - ); - if (findUniprotId) { - this.message = ''; - this.uniprotIdErrorMsg = 'The Uniprot ID already exists!'; - this.isUniProtIdError = true; - return; - } - - this.isError = false; - if (this.isDev) { - host = require('./js/const').devApiHost; - } - let url = `${host}/api/update-items`; - let data; - if (this.getUserData === 'Maintainer') { - data = { - Entity: 'condensate', - EntityId: response.data.canonical_id, - ChangeOperation: 'add', - Attribute: 'proteins', - Value: this.uniprotId, - SubmissionComments: - 'Maintainer do not need to provide a reason for such change at the moment!', - Status: 'accepted', - }; - } else { - if (this.proteinComment === '' || this.proteinComment.length < 50) { - this.proteinCommentErr = true; - this.commentErrorMsg = - 'Reason should not be empty or less than 50 characters!'; - - return; - } - data = { - Entity: 'condensate', - EntityId: response.data.canonical_id, - ChangeOperation: 'add', - Attribute: 'proteins', - Value: this.uniprotId, - SubmissionComments: this.proteinComment, - Status: 'requested', - }; - } - this.isLoading = true; - try { - await this.axios.post( - url, - { data: data }, - { - headers: { - Authorization: `Bearer ${this.jwt}`, - }, - } - ); - - this.isLoading = false; - this.toasterIsOpen = true; - - this.error = false; - this.isSubmitted = true; - this.uniprotId = ''; - this.comment = ''; - this.proteinCommentErr = false; - this.commentErrorMsg = ''; - this.updatedKey += 1; - setTimeout(() => { - this.toasterIsOpen = false; - }, 2000); - } catch (e) { - console.error(e); - this.message = - e.message || 'Something went wrong, please try again later!'; - this.isSubmitted = true; - this.error = true; - } - - // try { - // const res=await this.axios.get( - // `${host} - // /api/update-item/findCondensate/${response.data.canonical_id}`, - - // { - // headers: { - // Authorization: `Bearer ${this.jwt}`, - // }, - // } - // ); - - // console.log(await res) - - // } catch (e) { - // console.error(e); - - // } - }, }, }; </script> diff --git a/web/src/components/TagsInput.vue b/web/src/components/TagsInput.vue new file mode 100644 index 0000000..c5ed0b7 --- /dev/null +++ b/web/src/components/TagsInput.vue @@ -0,0 +1,94 @@ +<template> + <vue-tags-input + v-model="labelTag" + placeholder="" + :tags="value" + :autocomplete-items="autocompleteItems" + :add-only-from-autocomplete="true" + :disabled="false" + @before-adding-tag="obj => addObj(obj)" + @before-deleting-tag="obj => removeObj(obj)" + /> +</template> + +<script> +import VueTagsInput from '@johmun/vue-tags-input'; +import _ from 'lodash'; +const apikey = require('./js/const').apikey; + +export default { + name: 'TagsInput', + components: { + VueTagsInput, + }, + props: { + value: { required: true }, + }, + data() { + return { + labelTag: '', + autocompleteItems: [], + isDev: process.env.NODE_ENV === 'development', + }; + }, + computed: { + }, + watch: { + labelTag: function (newVal, oldVal) { + const vm = this; + vm.search(newVal); + } + }, + methods: { + addObj(obj) { + const vm = this; + this.$store.dispatch('Param/addProtein', { proteinName: obj.tag.proteinName }); + vm.labelTag = ''; + }, + removeObj(obj) { + this.$emit('input', obj); + }, + search(query) { + const vm = this + + let host = vm.isDev + ? require('./js/const').devHost + : require('./js/const').host; + + let url = `${host}/proteins?fields=uniprot_id,gene_name,name&query=${query}&size=10000`; + + fetch(url, { + method: 'GET', + mode: 'cors', + cache: 'no-cache', + headers: { + Authorization: `Bearer ${apikey}`, + }, + }) + .then((response) => response.json()) + .then((json) => { + vm.autocompleteItems = []; + vm.autocompleteItems = _.uniq(_.map(json.data, (c) => { + return {'text': c.uniprot_id, 'name': c.name, 'gene_name': c.gene_name, 'proteinName': c.uniprot_id} + }), 'text') + }) + }, + }, +}; +</script> + +<style> +.vue-tags-input .ti-input { + border: none; + border-bottom: 1px solid #a4b1b6; + padding: 4px; +} + +.ti-new-tag-input-wrapper[data-v-61d92e31] { + display: flex; + flex: 1 0 auto; + padding: 0px; + margin: 2px; + font-size: .85em; +} +</style> \ No newline at end of file diff --git a/web/src/router/index.js b/web/src/router/index.js index 99d51cf..54a71e4 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -132,6 +132,11 @@ export default new Router({ name: 'updateUser', component: () => import('@/views/UpdateUser'), }, + // { + // path: '/test/:protein', + // name: 'test', + // component: () => import('@/components/AddNovelCondensate'), + // }, // { path: '/user/:id', component: User }, // { // path: '*', diff --git a/web/src/store/modules/Param.js b/web/src/store/modules/Param.js new file mode 100644 index 0000000..5457583 --- /dev/null +++ b/web/src/store/modules/Param.js @@ -0,0 +1,46 @@ +const namespaced = true + +const state = { + proteinList: [], +} + +const mutations = { + ADD_PROTEIN (state, protein) { + if(state.proteinList.findIndex(d => d.uniprot_id === protein.uniprot_id) < 0) { + state.proteinList.push({uniprot_id: protein.uniprot_id, + functional_type: protein.functional_type, + proteins: protein.proteins, + pubmed_ids: protein.pubmed_ids, + driver_criterion: protein.driver_criterion, + experimental_evidences: protein.experimental_evidences, + }) + } + }, + REMOVE_PROTEIN (state, proteinIdx) { + state.proteinList.splice(proteinIdx, 1); + }, +} + +const getters = { + proteinList: state => state.proteinList, +} + +const actions = { + setProteinList ({ commit }, proteinList) { + commit('SET_PROTEIN_LIST', proteinList) + }, + addProtein ({ commit }, protein) { + commit('ADD_PROTEIN', protein) + }, + removeProtein ({ commit }, protein) { + commit('REMOVE_PROTEIN', protein) + } +} + +export default { + namespaced, + state, + mutations, + getters, + actions +} -- GitLab