diff --git a/src/frontend/static/frontend/src/components/biomarkers/BiomarkerTagPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/BiomarkerTagPanel.tsx new file mode 100644 index 00000000..2c346713 --- /dev/null +++ b/src/frontend/static/frontend/src/components/biomarkers/BiomarkerTagPanel.tsx @@ -0,0 +1,96 @@ +import React from 'react' +import { Segment, Header, Icon, List } from 'semantic-ui-react' +import { DjangoTag } from '../../utils/django_interfaces' +import { Nullable } from '../../utils/interfaces' +import { TagForm } from '../common/TagForm' + +/** + * Component's props + */ +interface BiomarkerTagsPanelProps { + newTag: DjangoTag, + tags: DjangoTag[], + selectedTag?: Nullable, + addingTag: boolean, + handleAddTagInputsChange: (name: string, value: any) => void + handleKeyDown: (any) => void, + confirmTagDeletion: (any) => void, + editTag: (any) => void, + handleFilterChanges?: (string, any) => void +} + +/** + * Renders a CRUD panel for Tags (model: api_service.models.Tag) + * @param props Component's props + * @returns Component + */ +export const BiomarkerTagsPanel = (props: BiomarkerTagsPanelProps) => { + const selectedTagId = props.selectedTag ? props.selectedTag.id : null + const tagsItems = props.tags.map((tag) => { + const isActive = tag.id === selectedTagId + + return ( + { + if (props.handleFilterChanges !== undefined) { + props.handleFilterChanges('tag', tag) + } + }} + className='clickable ellipsis' + > + + {/* Edit button */} + props.editTag(tag)} + /> + + {/* Delete button */} + props.confirmTagDeletion(tag)} + /> + + + + {tag.name} + {tag.description} + + + ) + }) + + return ( + +
+ + My Tags +
+ + {/* Add tag input */} + + + {/* Tag List */} + + {tagsItems} + +
+ ) +} diff --git a/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx b/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx index 177baab3..1dc764cc 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/BiomarkersPanel.tsx @@ -1,10 +1,10 @@ import React from 'react' import { Base } from '../Base' -import { Header, Button, Modal, Table, DropdownItemProps, Icon, Confirm, Form } from 'semantic-ui-react' -import { DjangoCGDSStudy, DjangoSurvivalColumnsTupleSimple, DjangoTag, DjangoUserFile, TagType } from '../../utils/django_interfaces' +import { Header, Button, Modal, Table, DropdownItemProps, Icon, Confirm, Form, Grid } from 'semantic-ui-react' +import { DjangoCGDSStudy, DjangoTag, DjangoUserFile, TagType } from '../../utils/django_interfaces' import ky, { Options } from 'ky' -import { getDjangoHeader, alertGeneralError, formatDateLocale, cleanRef, getFilenameFromSource, makeSourceAndAppend, getDefaultSource } from '../../utils/util_functions' -import { NameOfCGDSDataset, Nullable, CustomAlert, CustomAlertTypes, SourceType, OkResponse, ConfirmModal } from '../../utils/interfaces' +import { getDjangoHeader, alertGeneralError, formatDateLocale, cleanRef, getFilenameFromSource, makeSourceAndAppend, getDefaultSource, getDefaultNewTag, copyObject } from '../../utils/util_functions' +import { Nullable, CustomAlert, CustomAlertTypes, SourceType, OkResponse, ConfirmModal } from '../../utils/interfaces' import { Biomarker, BiomarkerType, BiomarkerOrigin, FormBiomarkerData, MoleculesSectionData, MoleculesTypeOfSelection, SaveBiomarkerStructure, SaveMoleculeStructure, FeatureSelectionPanelData, SourceStateBiomarker, FeatureSelectionAlgorithm, FitnessFunction, FitnessFunctionParameters, BiomarkerState, AdvancedAlgorithm as AdvancedAlgorithmParameters, BBHAVersion, BiomarkerSimple } from './types' import { ManualForm } from './modalContentBiomarker/manualForm/ManualForm' import { PaginatedTable, PaginationCustomFilter } from '../common/PaginatedTable' @@ -18,7 +18,6 @@ import { BiomarkerStateLabel } from './labels/BiomarkerStateLabel' import { BiomarkerOriginLabel } from './BiomarkerOriginLabel' import { BiomarkerDetailsModal } from './BiomarkerDetailsModal' import { getDefaultClusteringParameters, getDefaultRFParameters, getDefaultSvmParameters, getNumberOfMoleculesOfBiomarker } from './utils' - import { StopExperimentButton } from '../pipeline/all-experiments-view/StopExperimentButton' import { DeleteButton } from '../common/DeleteButton' import { SharedUsersBiomarker, SharedUsersBiomarkerPropsExtend } from './SharedUsersBiomarker' @@ -26,6 +25,7 @@ import { SharedInstitutionsBiomarker, SharedInstitutionsBiomarkerPropsExtend } f import { EditBiomarkerIcon } from './EditBiomarkerIcon' import { SwitchPublicButton } from '../common/SwitchPublicButton' import { PopupIcons } from '../common/PopupIcons' +import { TagsPanel } from '../files-manager/TagsPanel' // URLs defined in biomarkers.html declare const urlBiomarkersCRUD: string @@ -52,7 +52,8 @@ type MoleculeFinderResult = { molecule: string, standard: string } /** Extremely simple struct of a Biomarker (useful for simple updates). */ type BiomarkerNameAndDesc = { name: string, - description: string + description: string, + tag?: { id: number } | null } /** Some flags to validate the Biomarkers form. */ @@ -63,8 +64,6 @@ type ValidationForm = { /** BiomarkersPanel's state */ interface BiomarkersPanelState { - biomarkers: BiomarkerSimple[], - newBiomarker: Biomarker, /** PK of the Biomarker that's being loaded. */ loadingFullBiomarkerId: Nullable, selectedBiomarkerToDeleteOrSync: Nullable, @@ -74,6 +73,9 @@ interface BiomarkersPanelState { deletingBiomarker: boolean, /** Indicates if there's a Biomarker being stopped. */ stoppingExperiment: boolean, + newTag: DjangoTag, + selectedTagToDelete: Nullable, + addingTag: boolean, /** Biomarker to stop. */ biomarkerToStop: Nullable, addingOrEditingBiomarker: boolean, @@ -91,14 +93,16 @@ interface BiomarkersPanelState { openDetailsModal: boolean, /** Selected Biomarker instance to show its details. */ selectedBiomarker: Nullable, + /** Alert structure to display messages. */ alert: CustomAlert, featureSelection: FeatureSelectionPanelData, submittingFSExperiment: boolean, - openDetailsModal2: boolean, /** modal to handle shared institutions */ modalInstitutions: SharedInstitutionsBiomarkerPropsExtend, /** modal to handle shared users */ modalUsers: SharedUsersBiomarkerPropsExtend, + showDeleteTagModal: boolean, + deletingTag: boolean, } /** @@ -110,8 +114,6 @@ export class BiomarkersPanel extends React.Component { + handleChangeInputForm = (value: string, name: 'biomarkerName' | 'biomarkerDescription' | 'tag') => { this.setState(prevState => ({ formBiomarker: { ...prevState.formBiomarker, @@ -1376,46 +1390,6 @@ export class BiomarkersPanel extends React.Component { - return !this.state.addingOrEditingBiomarker && - this.state.newBiomarker.name.trim().length > 0 - } - - /** - * Handles Biomarker form changes - * @param name Name of the state field to modify - * @param value Value to set to the state field - */ - handleFormChanges = (name: string, value) => { - const newBiomarker = this.state.newBiomarker - newBiomarker[name] = value - this.setState({ newBiomarker }) - } - - /** - * TODO: Check if needed - * Adds a Survival data tuple for a CGDSDataset - * @param datasetName Name of the edited CGDS dataset - */ - addSurvivalFormTuple = (datasetName: NameOfCGDSDataset) => { - const newBiomarker = this.state.newBiomarker - const dataset = newBiomarker[datasetName] - - if (dataset !== null) { - const newElement: DjangoSurvivalColumnsTupleSimple = { event_column: '', time_column: '' } - - if (dataset.survival_columns === undefined) { - dataset.survival_columns = [] - } - - dataset.survival_columns.push(newElement) - this.setState({ newBiomarker }) - } - } - /** * Generates the modal to confirm a biomarker deletion * @returns Modal component. Null if no Tag was selected to delete @@ -1767,6 +1700,153 @@ export class BiomarkersPanel extends React.Component +
+ +

Are you sure you want to delete the Tag "{this.state.selectedTagToDelete.name}"?

+
+ + + + + + ) + } + + /** + * Handles New Tag Input changes + * @param name State field to change + * @param value Value to assign to the specified field + */ + handleAddTagInputsChange = (name: string, value) => { + const newTag = this.state.newTag + newTag[name] = value + this.setState(prevState => ({ + newTag: { + ...prevState.newTag, + [name]: value, + } + })) + } + + /** + * Makes a request to delete a Tag + */ + deleteTag = () => { + if (this.state.selectedTagToDelete === null) { + return + } + + // Sets the Request's Headers + const myHeaders = getDjangoHeader() + const deleteURL = `${urlTagsCRUD}${this.state.selectedTagToDelete.id}` + this.setState({ deletingTag: true }, () => { + ky.delete(deleteURL, { headers: myHeaders }).then((response) => { + // If OK is returned refresh the tags + if (response.ok) { + this.setState({ + deletingTag: false, + showDeleteTagModal: false + }) + this.getUserTags() + } + }).catch((err) => { + this.setState({ deletingTag: false }) + alertGeneralError() + console.log('Error deleting Tag ->', err) + }) + }) + } + + /** + * Handles New Tag Input Key Press + * @param e Event of change + */ + handleKeyDown = (e) => { + // If pressed Enter key submits the new Tag + if (e.which === 13 || e.keyCode === 13) { + this.addOrEditTag() + } else { + if (e.which === 27 || e.keyCode === 27) { + this.setState({ newTag: getDefaultNewTag() }) + } + } + } + + /** + * Show a modal to confirm a Tag deletion + * @param tag Selected Tag to delete + */ + confirmTagDeletion = (tag: DjangoTag) => { + this.setState({ + selectedTagToDelete: tag, + showDeleteTagModal: true + }) + } + + /** + * Does a request to add a new Tag + */ + addOrEditTag () { + if (this.state.addingTag) { + return + } + + // Sets the Request's Headers + const myHeaders = getDjangoHeader() + + // If exists an id then we are editing, otherwise It's a new Tag + let addOrEditURL, requestMethod + + if (this.state.newTag.id !== null) { + addOrEditURL = `${urlTagsCRUD}${this.state.newTag.id}/` + requestMethod = ky.patch + } else { + addOrEditURL = urlTagsCRUD + requestMethod = ky.post + } + + this.setState({ addingTag: true }, () => { + requestMethod(addOrEditURL, { headers: myHeaders, json: this.state.newTag }).then((response) => { + this.setState({ addingTag: false }) + response.json().then((responseJSON: DjangoTag) => { + if (responseJSON && responseJSON.id) { + // If all is OK, resets the form and gets the User's tag to refresh the list + this.setState({ newTag: getDefaultNewTag() }) + this.getUserTags() + } + }).catch((err) => { + alertGeneralError() + console.log('Error parsing JSON ->', err) + }) + }).catch((err) => { + this.setState({ addingTag: false }) + alertGeneralError() + console.log('Error adding new Tag ->', err) + }) + }) + } + + /** + * Selects a new Tag to edit + * @param selectedTag Tag to edit + */ + editTag = (selectedTag: DjangoTag) => { this.setState({ newTag: copyObject(selectedTag) }) } + render () { // Biomarker deletion modal const deletionConfirmModal = this.getDeletionConfirmModal() @@ -1774,6 +1854,14 @@ export class BiomarkersPanel extends React.Component { + const id = tag.id as number + return { key: id, value: id, text: tag.name } + }) + + tagOptions.unshift({ key: 'no_tag', text: 'No tag' }) return ( {/* Biomarker deletion modal */} @@ -1782,328 +1870,374 @@ export class BiomarkersPanel extends React.Component - headerTitle='Biomarkers' - headers={[ - { name: 'Name', serverCodeToSort: 'name', width: 3 }, - { name: 'Description', serverCodeToSort: 'description', width: 4 }, - { name: 'Tag', serverCodeToSort: 'tag' }, - { name: 'State', serverCodeToSort: 'state', textAlign: 'center' }, - { name: 'Origin', serverCodeToSort: 'origin', textAlign: 'center' }, - { name: 'Date', serverCodeToSort: 'upload_date' }, - { name: '# mRNAS', serverCodeToSort: 'number_of_mrnas', width: 1 }, - { name: '# miRNAS', serverCodeToSort: 'number_of_mirnas', width: 1 }, - { name: '# CNA', serverCodeToSort: 'number_of_cnas', width: 1 }, - { name: '# Methylation', serverCodeToSort: 'number_of_methylations', width: 1 }, - { name: 'Public', width: 1 }, - { name: 'Shared', width: 1 }, - { name: 'Actions', width: 2 } - ]} - defaultSortProp={{ sortField: 'upload_date', sortOrderAscendant: false }} - customFilters={this.getDefaultFilters()} - showSearchInput - customElements={[ - - - - ]} - searchLabel='Name' - searchPlaceholder='Search by name' - urlToRetrieveData={urlBiomarkersCRUD} - updateWSKey='update_biomarkers' - mapFunction={(biomarker: BiomarkerSimple) => { - const showNumberOfMolecules = biomarker.state === BiomarkerState.COMPLETED - const canEditMolecules = this.canEditBiomarker(biomarker) - const currentBiomarkerIsLoading = biomarker.id === this.state.loadingFullBiomarkerId - const isInProcess = biomarker.state === BiomarkerState.IN_PROCESS || + {/* Tag deletion modal */} + {tagDeletionConfirmModal} + + + + + + + + this.handleCancelConfirmModalState()} + onConfirm={() => { + this.handleCancelConfirmModalState() + this.state.confirmModal.onConfirm() + }} + /> + + + {/* Files overview panel */} + + + headerTitle='Biomarkers' + headers={[ + { name: 'Name', serverCodeToSort: 'name', width: 3 }, + { name: 'Description', serverCodeToSort: 'description', width: 4 }, + { name: 'Tag', serverCodeToSort: 'tag' }, + { name: 'State', serverCodeToSort: 'state', textAlign: 'center' }, + { name: 'Origin', serverCodeToSort: 'origin', textAlign: 'center' }, + { name: 'Date', serverCodeToSort: 'upload_date' }, + { name: '# mRNAS', serverCodeToSort: 'number_of_mrnas', width: 1 }, + { name: '# miRNAS', serverCodeToSort: 'number_of_mirnas', width: 1 }, + { name: '# CNA', serverCodeToSort: 'number_of_cnas', width: 1 }, + { name: '# Methylation', serverCodeToSort: 'number_of_methylations', width: 1 }, + { name: 'Public', width: 1 }, + { name: 'Shared', width: 1 }, + { name: 'Actions', width: 2 } + ]} + defaultSortProp={{ sortField: 'upload_date', sortOrderAscendant: false }} + customFilters={this.getDefaultFilters()} + showSearchInput + customElements={[ + + + + ]} + searchLabel='Name' + searchPlaceholder='Search by name' + urlToRetrieveData={urlBiomarkersCRUD} + updateWSKey='update_biomarkers' + mapFunction={(biomarker: BiomarkerSimple) => { + const showNumberOfMolecules = biomarker.state === BiomarkerState.COMPLETED + const canEditMolecules = this.canEditBiomarker(biomarker) + const currentBiomarkerIsLoading = biomarker.id === this.state.loadingFullBiomarkerId + const isInProcess = biomarker.state === BiomarkerState.IN_PROCESS || biomarker.state === BiomarkerState.WAITING_FOR_QUEUE - return ( - - - - - - - - {showNumberOfMolecules ? biomarker.number_of_mrnas : '-'} - {showNumberOfMolecules ? biomarker.number_of_mirnas : '-'} - {showNumberOfMolecules ? biomarker.number_of_cnas : '-'} - {showNumberOfMolecules ? biomarker.number_of_methylations : '-'} - - { - biomarker.is_public - ? ( + return ( + + + + + + + + {showNumberOfMolecules ? biomarker.number_of_mrnas : '-'} + {showNumberOfMolecules ? biomarker.number_of_mirnas : '-'} + {showNumberOfMolecules ? biomarker.number_of_cnas : '-'} + {showNumberOfMolecules ? biomarker.number_of_methylations : '-'} + + { + biomarker.is_public + ? ( + + ) + : ( + + ) + } + + + + + + + {/* Users can modify or delete own biomarkers or the ones which the user is admin of */} + <> + {/* Details button */} + this.openBiomarkerDetailsModal(biomarker)} /> - ) - } - - - - - - - {/* Users can modify or delete own biomarkers or the ones which the user is admin of */} - <> - {/* Details button */} - this.openBiomarkerDetailsModal(biomarker)} - /> - - {/* Edit button */} - - - {/* Clone button */} - this.setState({ biomarkerToClone: biomarker })} - /> - - {/* Stop button */} - {isInProcess && ( - this.setState({ biomarkerToStop: biomarker })} - /> - )} - {/* Delete button */} - {!isInProcess && !biomarker.is_public && ( - this.confirmBiomarkerDeletion(biomarker)} - ownerId={biomarker.user.id} - /> - )} - {/* Public switch */} - { - biomarker.id && ( - + + {/* Clone button */} + this.setState({ biomarkerToClone: biomarker })} /> - ) - } - - )} - /> - - - - ) - }} - /> - - {/* Create/Edit modal. */} - -
- - Are you sure you want to clone the Biomarker "{this.state.biomarkerToClone?.name}"? - - - - - - - - {/* Create/Edit modal. */} - } - closeOnEscape={false} - closeOnDimmerClick={false} - closeOnDocumentClick={false} - className={this.state.biomarkerTypeSelected !== BiomarkerOrigin.BASE ? 'space-modal large-modal' : undefined} - style={this.state.biomarkerTypeSelected === BiomarkerOrigin.BASE ? { width: '60%', minHeight: '60%' } : undefined} - onClose={() => { - if (this.state.biomarkerTypeSelected !== BiomarkerOrigin.BASE) { - this.handleChangeConfirmModalState( - true, - 'You are going to lose all the data inserted', - 'Are you sure?', - this.closeBiomarkerModal - ) - } else { - this.closeBiomarkerModal() - } - }} - > - {this.state.biomarkerTypeSelected === BiomarkerOrigin.BASE && - } - - {this.state.biomarkerTypeSelected === BiomarkerOrigin.MANUAL && ( - this.setState({ biomarkerToStop: biomarker })} + /> + )} + + {/* Delete button */} + {!isInProcess && !biomarker.is_public && ( + this.confirmBiomarkerDeletion(biomarker)} + ownerId={biomarker.user.id} + /> + )} + {/* Public switch */} + { + biomarker.id && ( + + ) + } + + )} + /> + + + + + ) + }} + /> + + {/* Create/Edit modal. */} + +
+ + Are you sure you want to clone the Biomarker "{this.state.biomarkerToClone?.name}"? + + + + + + + + {/* Create/Edit modal. */} + } + closeOnEscape={false} + closeOnDimmerClick={false} + closeOnDocumentClick={false} + className={this.state.biomarkerTypeSelected !== BiomarkerOrigin.BASE ? 'space-modal large-modal' : undefined} + style={this.state.biomarkerTypeSelected === BiomarkerOrigin.BASE ? { width: '60%', minHeight: '60%' } : undefined} + onClose={() => { + if (this.state.biomarkerTypeSelected !== BiomarkerOrigin.BASE) { + this.handleChangeConfirmModalState( + true, + 'You are going to lose all the data inserted', + 'Are you sure?', + this.closeBiomarkerModal + ) + } else { + this.closeBiomarkerModal() + } + }} + > + {this.state.biomarkerTypeSelected === BiomarkerOrigin.BASE && + } + + {this.state.biomarkerTypeSelected === BiomarkerOrigin.MANUAL && ( + { + const id = tag.id as number + return { key: id, value: id, text: tag.name } + }) + ]} + /> + )} + + {this.state.biomarkerTypeSelected === BiomarkerOrigin.FEATURE_SELECTION && ( + this.handleChangeConfirmModalState(true, 'You are going to lose all the data inserted', 'Are you sure?', this.closeBiomarkerModal)} + /> + )} + + + {/* Biomarker details modal. */} + } + closeOnEscape={false} + closeOnDimmerClick={false} + closeOnDocumentClick={false} + centered={false} + onClose={this.closeBiomarkerDetailsModal} + open={this.state.openDetailsModal} + > + + + + this.handleCancelConfirmModalState()} + onConfirm={() => { + this.state.confirmModal.onConfirm() + + this.setState(prevState => { + return { + confirmModal: { + ...prevState.confirmModal, + confirmModal: false + } + } + }) + }} + /> + - )} - - {this.state.biomarkerTypeSelected === BiomarkerOrigin.FEATURE_SELECTION && ( - this.handleChangeConfirmModalState(true, 'You are going to lose all the data inserted', 'Are you sure?', this.closeBiomarkerModal)} + - )} - - - {/* Biomarker details modal. */} - } - closeOnEscape={false} - closeOnDimmerClick={false} - closeOnDocumentClick={false} - centered={false} - onClose={this.closeBiomarkerDetailsModal} - open={this.state.openDetailsModal} - > - - - - this.handleCancelConfirmModalState()} - onConfirm={() => { - this.state.confirmModal.onConfirm() - - this.setState(prevState => { - return { - confirmModal: { - ...prevState.confirmModal, - confirmModal: false - } - } - }) - }} - /> - - - - + + + + ) } diff --git a/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/ManualForm.tsx b/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/ManualForm.tsx index 6fd318c0..6fe5765c 100644 --- a/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/ManualForm.tsx +++ b/src/frontend/static/frontend/src/components/biomarkers/modalContentBiomarker/manualForm/ManualForm.tsx @@ -1,11 +1,14 @@ import React from 'react' -import { Grid } from 'semantic-ui-react' +import { DropdownItemProps, Grid } from 'semantic-ui-react' import { BiomarkerType, FormBiomarkerData, MoleculesSectionData, MoleculesTypeOfSelection } from './../../types' import { NewBiomarkerForm } from './newBiomarkerForm/NewBiomarkerForm' import { MoleculesSectionsContainer } from './MoleculeSectionContainer' +import { DjangoTag } from '../../../../utils/django_interfaces' /** ManualForm's props. */ interface ManualFormProps { + tagOptions: DropdownItemProps[] + tags: DjangoTag[] biomarkerForm: FormBiomarkerData, /** Value for Checkbox. */ checkedIgnoreProposedAlias: boolean, @@ -26,7 +29,7 @@ interface ManualFormProps { handleValidateForm: () => { haveAmbiguous: boolean, haveInvalid: boolean }, handleSendForm: () => void, handleChangeCheckBox: (value: boolean) => void, - handleChangeInputForm: (value: string, name: 'biomarkerName' | 'biomarkerDescription') => void, + handleChangeInputForm: (value: any, name: 'biomarkerName' | 'biomarkerDescription' | 'tag') => void, } @@ -38,7 +41,6 @@ export const ManualForm = (props: ManualFormProps) => { handleChangeInputForm={props.handleChangeInputForm} biomarkerForm={props.biomarkerForm} cleanForm={props.cleanForm} - isFormEmpty={props.isFormEmpty} checkedIgnoreProposedAlias={props.checkedIgnoreProposedAlias} handleChangeIgnoreProposedAlias={props.handleChangeIgnoreProposedAlias} handleChangeMoleculeSelected={props.handleChangeMoleculeSelected} @@ -50,6 +52,8 @@ export const ManualForm = (props: ManualFormProps) => { handleValidateForm={props.handleValidateForm} handleSendForm={props.handleSendForm} handleChangeCheckBox={props.handleChangeCheckBox} + tagOptions={props.tagOptions} + tags={props.tags} /> void, - isFormEmpty: () => boolean, cleanForm: () => void, handleChangeMoleculeSelected: (name: BiomarkerType) => void, handleChangeMoleculeInputSelected: (value: MoleculesTypeOfSelection) => void, @@ -50,7 +52,7 @@ interface NewBiomarkerFormProps { handleValidateForm: () => { haveAmbiguous: boolean, haveInvalid: boolean }, handleSendForm: () => void, handleChangeCheckBox: (value: boolean) => void, - handleChangeInputForm: (value: string, name: 'biomarkerName' | 'biomarkerDescription') => void, + handleChangeInputForm: (value: any, name: 'biomarkerName' | 'biomarkerDescription' | 'tag') => void, } /** @@ -111,6 +113,22 @@ export const NewBiomarkerForm = (props: NewBiomarkerFormProps) => { onChange={(_, { value }) => props.handleChangeMoleculeSelected(Object.values(BiomarkerType).includes(value as BiomarkerType) ? value as BiomarkerType : BiomarkerType.MRNA)} /> + { + const selectedTag = props.tags.find(t => t.id === value) ?? null + props.handleChangeInputForm(selectedTag, 'tag') + }} + value={props.biomarkerForm.tag?.id ?? ''} + placeholder='Tag (optional)' + /> + { + handleChangeInputForm = (value: any, name: 'biomarkerName' | 'biomarkerDescription' | 'tag') => { const formBiomarker = this.state.formBiomarker formBiomarker[name] = value this.setState({ formBiomarker }) @@ -1295,45 +1296,6 @@ export class BiomarkerFromCorrelationModal extends React.Component { - const newBiomarker = this.state.newBiomarker - const dataset = newBiomarker[datasetName] - - if (dataset !== null && dataset.survival_columns !== undefined) { - dataset.survival_columns.splice(idxSurvivalTuple, 1) - this.setState({ newBiomarker }) - } - } - - /** - * TODO: Check if needed - * Handles CGDS Dataset form changes in fields of Survival data tuples - * @param datasetName Name of the edited CGDS dataset - * @param idxSurvivalTuple Index in survival tuple - * @param name Field of the CGDS dataset to change - * @param value Value to assign to the specified field - */ - handleSurvivalFormDatasetChanges = ( - datasetName: NameOfCGDSDataset, - idxSurvivalTuple: number, - name: string, - value: any - ) => { - const newBiomarker = this.state.newBiomarker - const dataset = newBiomarker[datasetName] - - if (dataset !== null && dataset.survival_columns !== undefined) { - dataset.survival_columns[idxSurvivalTuple][name] = value - this.setState({ newBiomarker }) - } - } - /** * Checks if the form is entirely empty. Useful to enable 'Cancel' button * @returns True is any of the form's field contains any data. False otherwise @@ -1562,6 +1524,8 @@ export class BiomarkerFromCorrelationModal extends React.Component