diff --git a/web/src/components/CMS/ExperimentalEvidence.vue b/web/src/components/CMS/ExperimentalEvidence.vue new file mode 100644 index 0000000000000000000000000000000000000000..c3b4e2fb71e2212c518d28de53606a5ee69c868c --- /dev/null +++ b/web/src/components/CMS/ExperimentalEvidence.vue @@ -0,0 +1,303 @@ +<template> + <div> + <base-toaster :open="toasterIsOpen" @close="hideDialog"> + <div class="flex justify-between items-center"> + <font-awesome-icon + class="ml-3" + icon="fa-solid fa-thumbs-up " + size="lg" + /> + + <h4>Request submitted successfully!</h4> + <button class="btn btn-outline" @click="hideDialog"> + <font-awesome-icon icon="fa-regular fa-circle-xmark" size="2x" /> + </button> + </div> + </base-toaster> + + <div> + <form + class="form-horizontal" + autocomplete="off" + @submit.prevent="proteinExperimentalHandler" + > + <div v-if="isLoading"> + <base-spinner /> + </div> + <div class="form-group"> + <div class="flex flex-col space-y-4 items-center"> + <div + class="w-2/3" + :class="{ invalid: !experimentalEvidance.isValid }" + > + <h1 + class=" + text-left + font-bold + mb-2 + text-xl + sm:text-2xl + font-montserrat + " + > + Select Experimental Evidence + </h1> + <div v-for="options in proteinExpEvidenceData" :key="options.id"> + <input + :id="options.id" + v-model="experimentalEvidance.value" + type="checkbox" + class="h-6 w-6" + :name="options" + :value="options.type" + @blur="clearValidity('experimentalEvidance')" + /> + <label class="mx-3" :for="options.id">{{ options.type }}</label> + </div> + <p v-if="!experimentalEvidance.isValid" class="text-red-500 mt-2"> + {{ experimentalEvidanceMsg }} + </p> + </div> + <div class="w-2/3" :class="{ invalid: !comment.isValid }"> + <textarea + v-model.trim="comment.value" + class=" + form-control + block + px-3 + py-1.5 + text-base + font-normal + text-gray-700 + bg-white bg-clip-padding + rounded + transition + ease-in-out + m-0 + focus:text-gray-700 + focus:bg-white + focus:border-blue-600 + focus:outline-none + " + rows="5" + :placeholder=" + role === 'Maintainer' ? 'Reason (Optional)' : 'Reason' + " + @blur="clearValidity('comment')" + /> + + <p v-if="!comment.isValid" class="text-red-500"> + Reason must not be empty or less than 50 characters. + </p> + <p v-if="serverError" class="text-danger font-bold"> + {{ errMessage }} + </p> + </div> + <div class="space-x-4 w-2/3"> + <button + class=" + text-white + bg-blue-600 + 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 + 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="close" + > + Cancel + </button> + </div> + </div> + </div> + </form> + </div> + </div> +</template> + +<script> +const _ = require("lodash"); +let host = require("../js/const").apiHost; +import BaseSpinner from "../UI/BaseSpinner.vue"; +import BaseToaster from "../UI/BaseToaster.vue"; +export default { + components: { BaseSpinner, BaseToaster }, + props: ["data", "uniprotId", "canonicalId"], + data() { + return { + isLoading: false, + toasterIsOpen: false, + formIsValid: true, + experimentalEvidance: { + value: [], + isValid: true, + }, + comment: { + value: "", + isValid: true, + }, + proteinExpEvidenceData: [ + { id: "1", type: "in_vitro" }, + { id: "2", type: "in_vivo" }, + { id: "3", type: "in_cellulo" }, + ], + experimentalEvidanceMsg: "", + serverError: false, + errMessage: "", + }; + }, + computed: { + jwt: function () { + return this.$store.getters["User/jwt"]; + }, + role: function () { + return this.$store.getters["User/userRole"]; + }, + }, + created() { + console.log("in evidence comp", this.data); + }, + methods: { + showDialog() { + this.toasterIsOpen = true; + }, + hideDialog() { + this.toasterIsOpen = false; + }, + close() { + this.$emit("closeProteinExpEvidence"); + }, + clearValidity(input) { + this[input].isValid = true; + }, + validateForm() { + this.formIsValid = true; + if (this.experimentalEvidance.value.length === 0) { + this.experimentalEvidanceMsg = "Please select a funtional type!"; + this.experimentalEvidance.isValid = false; + this.formIsValid = false; + } + if ( + (this.comment.value === "" || this.comment.value.length < 50) && + this.role !== "Maintainer" + ) { + this.comment.isValid = false; + this.formIsValid = false; + } + let findExpData = []; + for (let selectedExp of this.experimentalEvidance.value) { + for (let presentExp of this.data) { + if (selectedExp === presentExp) { + findExpData.push(selectedExp); + } + } + } + if (findExpData.length > 0) { + this.experimentalEvidanceMsg = `Selected experimental evidence ${findExpData.map( + (e) => e.toUpperCase() + )} is already in this protein. Please select another value to add.`; + this.experimentalEvidance.isValid = false; + this.formIsValid = false; + } + }, + async proteinExperimentalHandler() { + this.validateForm(); + if (!this.formIsValid) { + return; + } + + if (this.isDev) { + host = require("../js/const").devApiHost; + } + + let url = `${host}/api/update-items`; + let data; + let entityId = `${this.canonicalId}==${this.uniprotId}`; + if (this.role === "Maintainer") { + data = { + Entity: "condensate_protein", + EntityId: entityId, + ChangeOperation: "add", + Attribute: "exp_evidence", + SubmissionComments: + "Maintainer do not need to provide a reason for such change at the moment!", + Value: this.experimentalEvidance.value.join(), + Status: "accepted", + }; + } else { + data = { + Entity: "condensate_protein", + EntityId: entityId, + ChangeOperation: "add", + Attribute: "exp_evidence", + SubmissionComments: this.comment.value, + Value: this.experimentalEvidance.value.join(), + 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.experimentalEvidance.value = []; + this.comment.value = ""; + this.serverError = false; + this.errMessage = ""; + this.$emit("update-key"); + setTimeout(() => { + this.toasterIsOpen = false; + }, 2000); + } catch (e) { + console.error(e); + this.serverError = true; + this.errMessage = + e.message || "Something went wrong, please try again later!"; + } + }, + }, +}; +</script> + +<style scoped> +.invalid h1, +.invalid label { + color: red; +} +.invalid input, +.invalid textarea, +.invalid select { + border: 1px solid red; +} +</style> \ No newline at end of file diff --git a/web/src/components/CMS/ProteinFunctionalType.vue b/web/src/components/CMS/ProteinFunctionalType.vue new file mode 100644 index 0000000000000000000000000000000000000000..27a3a5c5dafa12d4621a8ea5615c2dd5d330664b --- /dev/null +++ b/web/src/components/CMS/ProteinFunctionalType.vue @@ -0,0 +1,316 @@ +<template> +<div> + <base-toaster + :open="toasterIsOpen" + @close="hideDialog" + > + <div class="flex justify-between items-center"> + <font-awesome-icon + class="ml-3" + icon="fa-solid fa-thumbs-up " + size="lg" + /> + + <h4>Request submitted successfully!</h4> + <button + class="btn btn-outline" + @click="hideDialog" + > + <font-awesome-icon + icon="fa-regular fa-circle-xmark" + size="2x" + /> + </button> + </div> + </base-toaster> + <div> + <form + class="form-horizontal" + autocomplete="off" + @submit.prevent="updateProteinFunctionalType" + > + <div v-if="isLoading"> + <base-spinner /> + </div> + <div class="form-group"> + <!-- <label + class="control-label col-sm-4" + for="FuncType" + >Choose a functional type</label> --> + <div class="flex flex-col space-y-4 items-center"> + <div class="w-2/3" :class="{ invalid: !functionalType.isValid }"> + <select + id="FuncType" + v-model.trim="functionalType.value" + class="form-control" + @blur="clearValidity('functionalType')" + > + <option + v-for="item in functionalOptions" + :key="item.id" + :value="item.type" + > + {{ item.type }} + </option> + </select> + + <p v-if="!functionalType.isValid" class="text-red-500 mt-2"> + {{functionalTypeMsg}} + </p> + </div> + + <div class="w-2/3" :class="{ invalid: !comment.isValid }"> + <textarea + + v-model.trim="comment.value" + :class="!comment.isValid ? 'border border-solid border-red-300' : ' border border-solid border-gray-300'" + class=" + form-control + block + px-3 + py-1.5 + text-base + font-normal + text-gray-700 + bg-white bg-clip-padding + + rounded + transition + ease-in-out + m-0 + focus:text-gray-700 + focus:bg-white + focus:border-blue-600 + focus:outline-none + " + rows="5" + :placeholder=" + role === 'Maintainer' ? 'Reason (Optional)' : 'Reason' + " + @blur="clearValidity('comment')" + /> + + <p + v-if="!comment.isValid" + class="text-red-500" + > + Reason must not be empty or less than 50 characters. + </p> + <p v-if="serverError" + class=" + text-danger font-bold" + > + {{ errMessage }} + </p> + + </div> + <div class="space-x-4 w-2/3"> + <button + class=" + text-white + bg-blue-600 + 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" + > + Update + </button> + <button + 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="close" + > + Cancel + </button> + </div> + </div> + </div> + </form> + </div> + </div> +</template> + +<script> +import BaseSpinner from '../UI/BaseSpinner.vue'; +import BaseToaster from '../UI/BaseToaster.vue'; +const _ = require("lodash"); +let host = require("../js/const").apiHost; +export default { + components: { BaseSpinner, BaseToaster }, + props: ["proteinFunctionalType", "uniprotId", "canonicalId"], + data() { + return { + isDev: process.env.NODE_ENV === "development", + formIsValid: true, + functionalType: { + value: this.proteinFunctionalType ? this.proteinFunctionalType : 'Choose a functional type', + isValid: true, + }, + functionalOptions: [ + { id: "1", type: "Choose a functional type" }, + { id: "2", type: "client" }, + { id: "3", type: "driver" }, + { id: "4", type: "regulator" }, + ], + comment: { + value: "", + isValid: true, + }, + functionalTypeMsg: "", + error: false, + errMsg: "", + serverError: false, + errMessage: "", + isLoading: false, + toasterIsOpen: false + }; + }, + mounted(){ + console.log("protein functional type is", this.uniprotId, this.canonicalId); + }, + computed: { + jwt: function () { + return this.$store.getters["User/jwt"]; + }, + role: function () { + return this.$store.getters["User/userRole"]; + }, + }, + methods: { + showDialog() { + this.toasterIsOpen = true; + }, + hideDialog() { + this.toasterIsOpen = false; + }, + close() { + this.$emit("closeToggleProteinFunctionalType"); + }, + selectValue() {}, + clearValidity(input){ + this[input].isValid = true + }, + validateForm() { + this.formIsValid = true; + if (this.functionalType.value === "" || this.functionalType.value === "Choose a functional type") { + this.functionalTypeMsg = "Please select a funtional type!" + this.functionalType.isValid = false; + this.formIsValid = false; + } + if(this.functionalType.value === this.proteinFunctionalType){ + this.functionalTypeMsg = `The selected functional type is already ${this.proteinFunctionalType}. Please select a different functional type from the list.` + this.functionalType.isValid = false; + this.formIsValid = false; + } + if((this.comment.value ==="" || this.comment.value.length < 50) && this.role !=="Maintainer" ){ + this.comment.isValid = false; + this.formIsValid = false + } + + }, + async updateProteinFunctionalType() { + + this.validateForm(); + if (!this.formIsValid) { + return; + } + + + if (this.isDev) { + host = require("../js/const").devApiHost; + } + + let url = `${host}/api/update-items`; + let data; + let entityId= `${this.canonicalId}==${this.uniprotId}` + if (this.role === 'Maintainer') { + data = { + Entity: 'condensate_protein', + EntityId: entityId, + ChangeOperation: 'update', + Attribute: 'functional_type', + SubmissionComments: + 'Maintainer do not need to provide a reason for such change at the moment!', + Value: this.functionalType.value, + Status: 'accepted', + }; + }else{ + data = { + Entity: 'condensate_protein', + EntityId: entityId, + ChangeOperation: 'update', + Attribute: 'functional_type', + SubmissionComments: this.comment.value, + Value: this.functionalType.value, + Status: 'requested', + }; + + } + + this.isValid = true; + this.error = ''; + + this.isLoading = true; + try { + await this.axios.post( + url, + { data: data }, + { + headers: { + Authorization: `Bearer ${this.jwt}`, + }, + } + ); + this.isLoading = false; + this.toasterIsOpen = true; + this.functionalType.value = this.proteinFunctionalType; + this.comment.value = ''; + this.isValid = true; + this.error = ''; + this.serverError = false; + this.errMessage = '' + this.$emit('update-key') + setTimeout(() => { + this.toasterIsOpen =false; + }, 2000); + } catch (e) { + console.error(e); + this.serverError = true; + this.errMessage = + e.message || 'Something went wrong, please try again later!'; + } + + }, + }, +}; +</script> + +<style scoped> +.invalid h1, +.invalid label { + color: red; +} +.invalid input, +.invalid textarea, +.invalid select { + border: 1px solid red; +} +</style> \ No newline at end of file diff --git a/web/src/components/CondensateDetailPage.vue b/web/src/components/CondensateDetailPage.vue index 395ce221fb549f7a54d017867d38e15850349497..b073b2466a43bace4ae62a22da9831881a9c3757 100644 --- a/web/src/components/CondensateDetailPage.vue +++ b/web/src/components/CondensateDetailPage.vue @@ -735,9 +735,18 @@ :is-experimental="response.data.is_experimental" :pubmed="response.data.protein_pubmed_ids" :db-tags="response.data.protein_source_db_tags" + :protein-driver-criterion="testProteinDriverCriterion" + :protein-experimental-evidence="testProteinExpEvidence" + + :protein-functional-type="testProteinFuntionalType" @update-key="updatedKey += 1" /> </div> + + <!-- for above llps table protein functiona type data --> + <!-- :protein-driver-criterion="response.data.protein_driver_criterion" + :protein-experimental-evidence="response.data.protein_exp_evidence" --> + <!-- :protein-functional-type="response.data.protein_functional_type" --> @@ -855,6 +864,25 @@ export default { props: ['condensateId'], data() { return { + // test data + testProteinFuntionalType:{ + "A0A178VY62": "client", + "A0A178W3G2": "driver", + "A0A1I9LN21": "regulator", + "A0A1I9LRM4": "driver", + }, + testProteinDriverCriterion: { + "A0A178VY62": ["self-ps"], + "A0A178W3G2": ["induce_formation"], + "A0A1I9LN21": ["self-ps"], + "A0A1I9LRM4": ["intergrity_essential", "self_ps"], + } , + testProteinExpEvidence:{ + "A0A178VY62": ["in_vivo"], + "A0A178W3G2": ["in_tergrity_essential_cellulo"], + "A0A1I9LN21": ["in_vitro"], + "A0A1I9LRM4": ["in_vivo", "in_vitro"], + } , condensate: this.$route.params.condensate ? this.$route.params.condensate : this.condensateId, diff --git a/web/src/components/CondensateUpdateItemsTable.vue b/web/src/components/CondensateUpdateItemsTable.vue index ea019a74ecc0760e41378529cae2e223f73110d9..cc297241d0862b4cd0ea3fa190f5f05153d925bb 100644 --- a/web/src/components/CondensateUpdateItemsTable.vue +++ b/web/src/components/CondensateUpdateItemsTable.vue @@ -65,11 +65,11 @@ export default { data: 'attributes.EntityId', fnCreatedCell: (nTd, sData, oData) => { if (oData.attributes.Entity === 'protein') { - $(nTd).html(`<a href='/protein/${sData}'> ${sData}</a>`); + $(nTd).html(`<a href='/protein/${sData}' class='resource-link'> ${sData}</a>`); } else if (oData.attributes.Entity === 'condensate') { - $(nTd).html(`<a href='/condensate/${sData}'> ${sData.length > 40 ? sData.substring(0, 40) + '..' : sData}</a>`); + $(nTd).html(`<a href='/condensate/${sData}' class='resource-link'> ${sData.length > 40 ? sData.substring(0, 40) + '..' : sData}</a>`); } else if (oData.attributes.Entity === 'condensate_protein') { - $(nTd).html(`<a href='/condensate/${sData.split('==')[0]}'> ${sData}</a>`); + $(nTd).html(`<a href='/condensate/${sData.split('==')[0]}' class='resource-link'> ${sData}</a>`); } else { $(nTd).html(`${sData}`); } @@ -360,7 +360,7 @@ export default { }; </script> -<style scoped> +<style> @import url("~@/assets/datatable.css"); select.form-control { @@ -383,6 +383,11 @@ ul.pagination { color: #0065b9; } +.resource-link{ + font-weight: bold; + color: #0065b9; +} + .pagination { font-size: 1.2rem; } diff --git a/web/src/components/DDCODE/fetchCondensate.vue b/web/src/components/DDCODE/fetchCondensate.vue index 1746dee0cd695a9ed7d9e638853ca51f7e347905..b6f3dd1f41b694b5b5ef1f37bedce4ebdc2975d9 100644 --- a/web/src/components/DDCODE/fetchCondensate.vue +++ b/web/src/components/DDCODE/fetchCondensate.vue @@ -61,7 +61,7 @@ import LoginVue from '../../views/Login.vue'; .then(response => response.json()) .then((response) => { // /* eslint-disable no-console */ - // console.log(response); + console.log(response); setTimeout(() => { vm.loading = false; diff --git a/web/src/components/LlpsTable.vue b/web/src/components/LlpsTable.vue index 7d618206a0221547d6d6c45a7a42a79132b3a079..5add6e0f04b3fb0a8e23cde833acfc92fdeef0b5 100644 --- a/web/src/components/LlpsTable.vue +++ b/web/src/components/LlpsTable.vue @@ -14,6 +14,8 @@ :pubmed="pubmed" :canonical-id="canonicaliD" :protein-evidence-score="proteinConfidenceScore" + :protein-functional-type="proteinFunctionalTypeData" + :protein-experimental-evidence="proteinExperimentalEvidenceData" @close="closeModal" /> <the-delete-modal @@ -92,6 +94,11 @@ export default { 'dbTags', 'isExperimental', 'canonicalId', + 'proteinDriverCriterion', + 'proteinExperimentalEvidence', + 'proteinFunctionalType', + + ], data() { return { @@ -101,13 +108,15 @@ export default { selectedRowData: '', canonicaliD: '', proteinConfidenceScore: 0, + proteinFunctionalTypeData: '', + proteinExperimentalEvidenceData: '' }; }, mounted() { const vm = this; vm.createTable(vm.id, vm.data); - // console.log("in table pubmed data", this.data); + }, methods: { closeModal() { @@ -345,6 +354,7 @@ export default { { title: 'Gene Name', data: 'gene_name', + className: 'text-nowrap', fnCreatedCell: (nTd, sData, oData) => { if (sData) { $(nTd).html(`<a href="" class="protein-link"> ${sData}</a>`); @@ -356,6 +366,7 @@ export default { { title: 'Name', data: 'name', + fnCreatedCell: (nTd, sData, oData) => { if (sData) { $(nTd).html(`<a href="" class="protein-link"> ${sData}</a>`); @@ -385,7 +396,7 @@ export default { { title: 'Pubmed', data: 'uniprot_id', - className: 'text-nowrap', + className: 'whitespace-normal', render: function (data, type, row, meta) { if (vm.pubmed) { let dat = vm.pubmed[row.uniprot_id]; @@ -415,6 +426,54 @@ export default { $(nTd).html(''); } }, + }, + { + title: 'Functional Type', + data: 'protein_functional_type', + className: 'whitespace-normal', + defaultContent: '<i>No data</i>', + render: function (data, type, row, meta) { + // console.log(_.flatMap(row.condensates, c => c.data_sources)) + if(!vm.proteinFunctionalType){ + return; + } + let dat = vm.proteinFunctionalType[row.uniprot_id]; + if (dat) { + return dat; + } else{ + return ''; + } + + }, + }, + { + title: 'Experimental Evidence', + data: 'protein_exp_evidence', + className: 'whitespace-normal', + defaultContent: '<i>No data</i>', + render: function (data, type, row, meta) { + // console.log(_.flatMap(row.condensates, c => c.data_sources)) + let dat = vm.proteinExperimentalEvidence[row.uniprot_id]; + + if (dat) { + return dat.join(', '); + } + return ''; + }, + }, + { + title: 'Driver Criterion', + data: 'driver_criterion', + className: 'whitespace-normal', + defaultContent: '<i>No data</i>', + render: function (data, type, row, meta) { + // console.log(_.flatMap(row.condensates, c => c.data_sources)) + let dat = vm.proteinDriverCriterion[row.uniprot_id]; + if (dat) { + return dat.join(', '); + } + return ''; + }, }, { title: 'DB', @@ -551,14 +610,30 @@ export default { const row = table.row(tr); const id = row.data().uniprot_id; let findScore; + for (let score in vm.map) { if (score === id) { findScore = vm.map[score]; } } - console.log('rating is', findScore); + let findProteinFunctionalType; + for (let type in vm.proteinFunctionalType) { + if (type === id) { + findProteinFunctionalType = vm.proteinFunctionalType[type]; + } + } + let proteinExpEvi; + for (let exp in vm.proteinExperimentalEvidence) { + if (exp === id) { + proteinExpEvi = vm.proteinExperimentalEvidence[exp]; + } + } + console.log('exp evidence is', proteinExpEvi); + vm.proteinFunctionalTypeData = findProteinFunctionalType; + vm.proteinExperimentalEvidenceData = proteinExpEvi; vm.proteinConfidenceScore = findScore; vm.selectedRowData = row.data(); + vm.uniprotId = id; vm.canonicaliD = vm.canonicalId; vm.toggleActionModal = true; diff --git a/web/src/components/ProteinUpdateItemTable.vue b/web/src/components/ProteinUpdateItemTable.vue index 7aa40aceee9fb6fbe2b81061d97c77b216d17742..4a8fb2c54f0c40a75bfd7228aeecf0e6a508b7e4 100644 --- a/web/src/components/ProteinUpdateItemTable.vue +++ b/web/src/components/ProteinUpdateItemTable.vue @@ -303,7 +303,7 @@ export default { }; </script> -<style scoped> +<style> @import url("~@/assets/datatable.css"); select.form-control { diff --git a/web/src/components/UI/TheActionModal.vue b/web/src/components/UI/TheActionModal.vue index d14173c911eb23a9313212ec1589b1bf219b28f2..8ec94c8279cc6175e657edd6e65f6a2efca0a6c5 100644 --- a/web/src/components/UI/TheActionModal.vue +++ b/web/src/components/UI/TheActionModal.vue @@ -121,6 +121,46 @@ Remove Pubmed ID </button> + <button + type="button" + class=" + text-white + font-bold + rounded-full + w-full + bg-blue-600 + hover:bg-blue-800 + focus:ring-4 focus:ring-blue-300 + rounded-lg + px-5 + py-4 + mr-2 + " + @click="openProteinFunctionalType" + > + Update Functional Type + </button> + + <button + type="button" + class=" + text-white + font-bold + rounded-full + w-full + bg-blue-600 + hover:bg-blue-800 + focus:ring-4 focus:ring-blue-300 + rounded-lg + px-5 + py-4 + mr-2 + " + @click="openProteinExperimentalEvidence" + > + Add Experimental Evidence + </button> + <button type="button" class=" @@ -161,6 +201,16 @@ type="Delete" @closeToggleDeletePubmed="backToList" /> + <protein-functional-type v-if="toggleProteinFuntionalType" + :protein-functional-type="proteinFunctionalType" + :uniprot-id="proteinUniprot" + :canonical-id="canonicalId" + @closeToggleProteinFunctionalType="backToList" /> + <experimental-evidence v-if="toggleProteinExperimentalEvidence" + :data="proteinExperimentalEvidence" + :uniprot-id="proteinUniprot" + :canonical-id="canonicalId" + @closeProteinExpEvidence="backToList"/> <evidence-star-rating v-if="toggleStarRating" :uniprot="proteinUniprot" @@ -179,9 +229,11 @@ <script> import addDeletePubmed from '../CMS/addDeletePubmed.vue'; import EvidenceStarRating from '../CMS/evidenceStarRating.vue'; +import ExperimentalEvidence from '../CMS/ExperimentalEvidence.vue'; +import ProteinFunctionalType from '../CMS/ProteinFunctionalType.vue'; export default { - components: { addDeletePubmed, EvidenceStarRating }, + components: { addDeletePubmed, EvidenceStarRating, ProteinFunctionalType, ExperimentalEvidence }, props: [ 'show', 'proteinUniprot', @@ -189,6 +241,8 @@ export default { 'pubmed', 'canonicalId', 'proteinEvidenceScore', + 'proteinFunctionalType', + 'proteinExperimentalEvidence' ], data() { return { @@ -196,16 +250,20 @@ export default { toggleAddPubId: false, toggleDeletePubId: false, toggleStarRating: false, + toggleProteinFuntionalType: false, + toggleProteinExperimentalEvidence: false }; }, created() { - console.log('in action modal', this.rowData); + console.log('in action modal', this.proteinExperimentalEvidence); }, methods: { backToList() { this.toggleAddPubId = false; this.toggleDeletePubId = false; this.toggleStarRating = false; + this.toggleProteinFuntionalType = false; + this.toggleProteinExperimentalEvidence= false; this.toggleList = true; }, @@ -220,6 +278,14 @@ export default { this.toggleList = false; this.toggleDeletePubId = true; }, + openProteinFunctionalType(){ + this.toggleList = false; + this.toggleProteinFuntionalType = true; + }, + openProteinExperimentalEvidence(){ + this.toggleList = false; + this.toggleProteinExperimentalEvidence = true; + }, openEvidenceStarRating() { this.toggleList = false; this.toggleStarRating = true; diff --git a/web/src/components/UpdateItemTable.vue b/web/src/components/UpdateItemTable.vue index 10a0daef85e6f76ac23983a43338addc8635d1ab..f69fd8b0cd96903e4487501a25aa96656c58216c 100644 --- a/web/src/components/UpdateItemTable.vue +++ b/web/src/components/UpdateItemTable.vue @@ -1,8 +1,13 @@ <template> + + + <table :id="id" class="table table-striped table-bordered table-hover" + style="width:100%" /> + </template> <script> @@ -46,6 +51,7 @@ export default { { title: 'ID', data: 'id', + className: 'whitespace-nowrap', fnCreatedCell: (nTd, sData, oData) => { $(nTd).html(`<a href="" class="edit-link"> ${sData}</a>`); }, @@ -53,17 +59,19 @@ export default { { title: 'Resource Name', data: 'attributes.Entity', + className: 'whitespace-nowrap', }, { title: 'Resource ID', data: 'attributes.EntityId', + className: 'whitespace-normal', fnCreatedCell: (nTd, sData, oData) => { if (oData.attributes.Entity === 'protein') { - $(nTd).html(`<a href='/protein/${sData}'> ${sData}</a>`); + $(nTd).html(`<a href='/protein/${sData}' class='resource-link'> ${sData}</a>`); } else if (oData.attributes.Entity === 'condensate') { - $(nTd).html(`<a href='/condensate/${sData}'> ${sData.length > 40 ? sData.substring(0, 40) + '..' : sData }</a>`); + $(nTd).html(`<a href='/condensate/${sData}' class='resource-link'> ${sData.length > 40 ? sData.substring(0, 40) + '..' : sData }</a>`); } else if (oData.attributes.Entity === 'condensate_protein') { - $(nTd).html(`<a href='/condensate/${sData.split('==')[0]}'> ${sData}</a>`); + $(nTd).html(`<a href='/condensate/${sData.split('==')[0]}' class='resource-link'> ${sData}</a>`); } else { $(nTd).html(`${sData}`); } @@ -80,6 +88,7 @@ export default { { title: 'Change Operation', data: 'attributes.ChangeOperation', + className: 'whitespace-normal', }, { title: 'Status', @@ -99,6 +108,7 @@ export default { { title: 'Submitted at', data: 'attributes.SubmittedAt', + className: 'whitespace-normal', render: function (data, type, row, meta) { return new Date(Date.parse(data)).toLocaleString(); }, @@ -106,6 +116,7 @@ export default { { title: 'Submitted by', data: 'attributes.submittedBy', + className: 'whitespace-normal', render: function (data, type, row, meta) { if (data.data) { return data.data.attributes.username; @@ -131,12 +142,8 @@ export default { const nTableOptions = { columns, - - - - // aaSorting: [[ 0, 'asc' ]], - lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]], + lengthMenu: [[10, 25, 50, -1], [10, 25, 50, 'All' ]], paging: true, searching: true, info: true, @@ -298,23 +305,16 @@ export default { }; </script> -<style scoped> +<style> @import url("~@/assets/datatable.css"); -select.form-control { - font-size: 12px; - padding: 3px 12px; -} + ul.pagination { font-size: 1rem; } -.form-inline .form-control { - height: 25px; - line-height: 25px; - vertical-align: middle; -} + .edit-link { font-weight: bold; @@ -334,4 +334,62 @@ ul.pagination { -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } + +table.dataTable.row-border > tbody > tr:first-child > th, +table.dataTable.row-border > tbody > tr:first-child > td, +table.dataTable.display > tbody > tr:first-child > th, +table.dataTable.display > tbody > tr:first-child > td { + border-top: none; +} + +table.dataTable.row-border > tbody > tr > th, +table.dataTable.row-border > tbody > tr > td, +table.dataTable.display > tbody > tr > th, +table.dataTable.display > tbody > tr > td { + border-top: 1px solid #ddd; +} + +table.dataTable > tbody > tr { + background-color: #ffffff; +} + +table.dataTable.row-border > tbody > tr:hover, +table.dataTable.display > tbody > tr:hover { + background-color: #f6f6f6; +} + +select.form-control { + font-size: 12px; + padding: 3px 12px; +} + + + +.form-inline .form-control { + height: 25px; + line-height: 25px; + vertical-align: middle; +} + +.resource-link { + font-weight: bold; + color: #0065b9; +} + +.uniprot-link { + font-weight: bold; + color: #0065b9; +} + +.pubmed-link { + font-weight: bold; + color: #0065b9; +} + +.external-link { + font-weight: bold; + color: #0065b9; +} + + </style> diff --git a/web/src/components/js/const.js b/web/src/components/js/const.js index 68137d10c18819c3cd0574658fe12f4ad29f193e..81a86ac6c232f73ff47c27397c89f5d35ea2306e 100644 --- a/web/src/components/js/const.js +++ b/web/src/components/js/const.js @@ -1,12 +1,12 @@ export const host = '/api'; -export const devHost = '/api'; -export const apiHost = '/cms'; -export const devApiHost = '/cms'; +// export const devHost = '/api'; +// export const apiHost = '/cms'; +// export const devApiHost = '/cms'; -// export const devHost = 'https://dev.ddcode.org/api'; + export const devHost = 'https://dev.ddcode.org/api'; // export const devHost = 'http://localhost:5001'; -// export const apiHost = 'http://localhost:1337'; -// export const devApiHost = 'http://localhost:1337'; + export const apiHost = 'http://localhost:1337'; + export const devApiHost = 'http://localhost:1337'; // apikey should be give here: export const apikey = process.env.VUE_APP_API_KEY; diff --git a/web/src/views/UpdateItems.vue b/web/src/views/UpdateItems.vue index 9c9de0d158308292e2c1fad3888d37874f4a6d57..0d9f698b8b2d83d1d58934cdd6ed1cfe0fc1e0a5 100644 --- a/web/src/views/UpdateItems.vue +++ b/web/src/views/UpdateItems.vue @@ -1,12 +1,12 @@ <template> - <div class="flex flex-wrap justify-center"> - <div class="w-5/6"> + <div class="flex justify-center"> + <div class="w-5/6 shrink"> <div v-if=" userData !== null && (userRole === 'Contributor' || userRole === 'Maintainer') " - class="my-14 border border-gray-300 rounded-lg p-8" + class="my-14 border w-full border-gray-300 rounded-lg p-8" > <update-item-table id="update-item-table" /> </div> @@ -65,10 +65,12 @@ export default { }, }; </script> -<style> +<style scoped> @import url("~@/assets/bootstrap.css"); - +div.tablecontainer{ + width: 80% +} a { color: #42b983;