diff --git a/api/controllers/UploadController.js b/api/controllers/UploadController.js index fed2ba2e..9d85c38d 100644 --- a/api/controllers/UploadController.js +++ b/api/controllers/UploadController.js @@ -34,6 +34,7 @@ exports.headerValues = [ patientIdTypesSpecResult, capturePanelResult, readLengthsResult, + squareSizesResult, ] = results; let currentApplications = applicationsResult.filter( (application) => !constants.deprecatedApplications.includes(application.toLowerCase()) @@ -47,6 +48,7 @@ exports.headerValues = [ patientIdTypes: patientIdTypesResult, patientIdTypesSpecified: patientIdTypesSpecResult, readLengths: readLengthsResult, + squareSizes: squareSizesResult, }; return apiResponse.successResponseWithData(res, 'Operation success', responseObject); }) diff --git a/api/models/SubmissionModel.js b/api/models/SubmissionModel.js index ac6a420b..51cf8e08 100644 --- a/api/models/SubmissionModel.js +++ b/api/models/SubmissionModel.js @@ -19,6 +19,7 @@ var FormSchema = new mongoose.Schema({ serviceId: { type: String, required: true }, species: { type: String, required: true }, sequencingReadLength: { type: String, required: false }, + squareSize: { type: String, required: false }, }); var SubmissionSchema = new mongoose.Schema( diff --git a/api/util/constants.js b/api/util/constants.js index f1ee561e..b3765ddb 100644 --- a/api/util/constants.js +++ b/api/util/constants.js @@ -1,6 +1,6 @@ export const constants = { containers: ['Plates', 'Micronic Barcoded Tubes', 'Blocks/Slides/Tubes'], - headerPicklists: ['Recipe', 'Exemplar+Sample+Types', 'Species', 'PatientIDTypes', 'PatientIdTypesSpecified', 'Bait+Selection+Choices', 'Illumina+Sequencing+Run+Types'], + headerPicklists: ['Recipe', 'Exemplar+Sample+Types', 'Species', 'PatientIDTypes', 'PatientIdTypesSpecified', 'Bait+Selection+Choices', 'Illumina+Sequencing+Run+Types', 'Square+Size'], containersByMaterial: { Tissue: ['Blocks/Slides/Tubes', 'Micronic Barcoded Tubes'], Cells: ['Plates', 'Blocks/Slides/Tubes'], diff --git a/api/util/helpers.js b/api/util/helpers.js index 013574b1..5de78894 100644 --- a/api/util/helpers.js +++ b/api/util/helpers.js @@ -663,6 +663,7 @@ export function submit(submission, user, transactionId) { let capturePanel = submission.formValues.capturePanel; let sampleType = submission.formValues.material; let seqReadLength = submission.formValues.sequencingReadLength || ''; + let squareSize = submission.formValues.squareSize || ''; let samples = submission.gridValues; let submittedSamples = []; // prep banked sample record @@ -678,6 +679,7 @@ export function submit(submission, user, transactionId) { bankedSample.user = process.env.API_USER; bankedSample.concentrationUnits = 'ng/uL'; bankedSample.sequencingReadLength = seqReadLength; + bankedSample.squareSize = squareSize; if (recipe.includes('COVID')) { bankedSample.userId = `${bankedSample.userId}-${serviceId}`; diff --git a/frontend/src/components/Upload/UploadForm.js b/frontend/src/components/Upload/UploadForm.js index fb98cd0a..bef5feca 100644 --- a/frontend/src/components/Upload/UploadForm.js +++ b/frontend/src/components/Upload/UploadForm.js @@ -26,6 +26,7 @@ class UploadForm extends React.Component { patientIdTypeSpecified: true, sharedWith: true, sequencingReadLength: true, + squareSize: true, }, }; } @@ -49,6 +50,9 @@ class UploadForm extends React.Component { this.state.values.patientIdType === 'Both MSK-Patients and Non-MSK Patients') ); }; + showSquareSpaceDropdown = () => { + return this.state.values.application === '10X_Genomics_Visium'; + }; showReadLengthDropdown = () => { // dont show anything until they select application if (this.state.values.application.length === 0) return false; @@ -152,6 +156,19 @@ class UploadForm extends React.Component { formValid[value] = isValidOption && values[value].length > 0; break; + case 'squareSize': + if (!this.showSquareSpaceDropdown()) { + formValid[value] = true; + values[value] = ''; + break; + } + isValidOption = this.props.form.squareSizes.some(function(el) { + return el === values[value]; + }); + + formValid[value] = isValidOption && values[value].length > 0; + break; + case 'sequencingReadLength': if (!this.showReadLengthDropdown()) { formValid[value] = true; @@ -270,7 +287,8 @@ class UploadForm extends React.Component { this.state.formValid.patientIdTypeSpecified && this.state.formValid.capturePanel && this.state.formValid.sharedWith && - this.state.formValid.sequencingReadLength + this.state.formValid.sequencingReadLength && + this.state.formValid.squareSize ); } @@ -283,6 +301,7 @@ class UploadForm extends React.Component { handleMaterialChange, handleSpeciesChange, handleReadLengthChange, + handleSquareSizeChange, gridIsLoading, nothingToChange, gridNumberOfSamples, @@ -329,6 +348,24 @@ class UploadForm extends React.Component { label: form.selected.application, }} /> + {this.showSquareSpaceDropdown() && ( + ({ + value: option, + label: option, + }))} + loading={form.formIsLoading} + dynamic + value={{ + value: form.selected.squareSize, + label: form.selected.squareSize, + }} + /> + )} {this.showReadLengthDropdown() && ( { + const { clearSquareSize } = this.props; + if (!selectedSquareSize) clearSquareSize(); + }; + handleReadLengthChange = (selectedReadLength) => { const { clearReadLengths } = this.props; if (!selectedReadLength) clearReadLengths(); - } + }; handleSpeciesChange = (selectedSpecies) => { const { clearSpecies } = this.props; @@ -78,6 +83,7 @@ export class UploadFormContainer extends Component { handleApplicationChange={this.handleApplicationChange} handleSpeciesChange={this.handleSpeciesChange} handleReadLengthChange={this.handleReadLengthChange} + handleSquareSizeChange={this.handleSquareSizeChange} handleInputChange={this.handleInputChange} handleClear={this.handleClear} /> diff --git a/frontend/src/redux/actions/upload/formActions.js b/frontend/src/redux/actions/upload/formActions.js index ded2dfe4..8b56d6ff 100644 --- a/frontend/src/redux/actions/upload/formActions.js +++ b/frontend/src/redux/actions/upload/formActions.js @@ -82,7 +82,7 @@ export const SELECT = 'SELECT'; export function select(id, value, checkForMismatch = true) { return (dispatch) => { - if (id === 'species' || id === 'container' || id === 'patientIdType' || id === 'patientIdTypeSpecified' || id === 'sequencingReadLength') { + if (id === 'species' || id === 'container' || id === 'patientIdType' || id === 'patientIdTypeSpecified' || id === 'sequencingReadLength' || id === 'squareSize') { checkForMismatch && dispatch(checkForChange(id, value)); return dispatch({ type: SELECT, @@ -185,7 +185,12 @@ export const clearSpecies = () => { export const CLEAR_READ_LENGTHS = 'CLEAR_READ_LENGTHS'; export const clearReadLengths = () => { - return [{ type: CLEAR_READ_LENGTHS }, { type: CLEARED }];; + return [{ type: CLEAR_READ_LENGTHS }, { type: CLEARED }]; +}; + +export const CLEAR_SQUARE_SIZE = 'CLEAR_SQUARE_SIZE'; +export const clearSquareSize = () => { + return [{ type: CLEAR_SQUARE_SIZE }, { type: CLEARED }]; }; export const CLEAR_MATERIAL = 'CLEAR_MATERIAL'; diff --git a/frontend/src/redux/reducers/initialState.js b/frontend/src/redux/reducers/initialState.js index 9207a82d..62f49b72 100644 --- a/frontend/src/redux/reducers/initialState.js +++ b/frontend/src/redux/reducers/initialState.js @@ -11,6 +11,7 @@ export const initialFormState = { patientIdTypes: [], patientIdTypesSpecified: [], readLengths: [], + squareSizes: [], allSpecies: [], filteredSpecies: [], @@ -29,6 +30,7 @@ export const initialFormState = { isShared: false, sharedWith: '', sequencingReadLength: '', + squareSize: '', }, // selected: { // application: 'MouseWholeGenome', diff --git a/frontend/src/redux/reducers/upload/formReducer.js b/frontend/src/redux/reducers/upload/formReducer.js index 08fd4835..aa38fbaf 100644 --- a/frontend/src/redux/reducers/upload/formReducer.js +++ b/frontend/src/redux/reducers/upload/formReducer.js @@ -27,6 +27,7 @@ export default function formReducer(state = initialFormState, action) { patientIdTypes: action.form_data.patientIdTypes, patientIdTypesSpecified: action.form_data.patientIdTypesSpecified, readLengths: action.form_data.readLengths, + squareSizes: action.form_data.squareSizes, }; case ActionTypes.RECEIVE_INITIAL_STATE_FAIL: @@ -228,6 +229,12 @@ export default function formReducer(state = initialFormState, action) { selected: { ...state.selected, application: '' }, formIsLoading: true, }; + case ActionTypes.CLEAR_SQUARE_SIZE: + return { + ...state, + selected: { ...state.selected, squareSize: '' }, + formIsLoading: true, + }; case ActionTypes.CLEAR_READ_LENGTHS: return { ...state, diff --git a/frontend/src/util/translations/en.json b/frontend/src/util/translations/en.json index 262bb6ec..78ca9b85 100644 --- a/frontend/src/util/translations/en.json +++ b/frontend/src/util/translations/en.json @@ -23,6 +23,8 @@ "groupIdType_label": "Patient or Group ID Type*", "sequencingReadLength_label": "Sequencing Read Length*", "sequencingReadLength_helptext": "Select Read Length", + "squareSize_label": "Square Size*", + "squareSize_helptext": "Which size square would you like for each sample?", "isShared_label": "I want others to be able to edit or submit these samples on my behalf.", "sharedWith_label": "Give permission to:",