'
+ '
'
);
LDK.Utils.getBasicQWP({
- title: 'MCC Animal Requests',
+ title: 'All Pending RAB Reviews',
+ schemaName: 'mcc',
+ queryName: 'requestReviews',
+ viewName: 'Pending Reviews',
+ showRecordSelectors: false,
+ showDetailsColumn: false
+ }).render(webpart.wrapperDivId + '-webpartAssignments');
+
+ LDK.Utils.getBasicQWP({
+ title: 'All Pending MCC Requests',
frame: 'left-nav',
schemaName: 'mcc',
- queryName: 'animalRequests',
- viewName: 'Submitted',
+ queryName: 'requestScores',
+ //viewName: 'Pending Requests',
showRecordSelectors: true,
- showDetailsColumn: true,
- buttonBar: {position: 'top', includeStandardButtons: true, items: []}
+ showDetailsColumn: true
}).render(webpart.wrapperDivId + '-webpartPending');
+
+ LDK.Utils.getBasicQWP({
+ title: 'Current RAB Members',
+ //frame: 'none',
+ schemaName: 'core',
+ queryName: 'Members',
+ columns: 'UserId,UserId/FirstName,UserId/LastName,UserId/Email',
+ filterArray: [LABKEY.Filter.create('GroupId/Name', 'MCC RAB Members')],
+ showRecordSelectors: false,
+ showDetailsColumn: false,
+ buttonBar: {position: 'top', includeStandardButtons: true, items: []}
+ }).render(webpart.wrapperDivId + '-webpartRAB');
});
\ No newline at end of file
diff --git a/mcc/resources/views/mccRequests.html b/mcc/resources/views/mccRequests.html
index 84037736d..e7652b3b6 100644
--- a/mcc/resources/views/mccRequests.html
+++ b/mcc/resources/views/mccRequests.html
@@ -7,7 +7,7 @@
return;
Ext4.get(webpart.wrapperDivId).update(
- 'Investigators interested in requesting marmosets for their research can submit applications via the animal request portal. Investigators must already have user access to the password protected MCC site to submit these applications. The animal request review process (submission to approval) takes about 10 business days. (Please note that applications are not being accepted yet).' +
+ 'The MCC is now accepting animal requests! Investigators interested in requesting marmosets for their research can submit applications via the animal request portal. Please note, while you are encouraged to submit your requests now, the review process will not begin until June 2022, and the first animals will not be available until at least September 2022.' +
'
' +
'
Submit New Animal Request' +
'
Click Here to View Documentation on the Request Process and Scoring Criteria' +
diff --git a/mcc/resources/views/nihReview.html b/mcc/resources/views/nihReview.html
new file mode 100644
index 000000000..007019081
--- /dev/null
+++ b/mcc/resources/views/nihReview.html
@@ -0,0 +1,29 @@
+
\ No newline at end of file
diff --git a/mcc/resources/views/nihReview.view.xml b/mcc/resources/views/nihReview.view.xml
new file mode 100644
index 000000000..02b85efcb
--- /dev/null
+++ b/mcc/resources/views/nihReview.view.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mcc/resources/views/rabRequestReview.html b/mcc/resources/views/rabRequestReview.html
new file mode 100644
index 000000000..459fbe186
--- /dev/null
+++ b/mcc/resources/views/rabRequestReview.html
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/mcc/resources/views/rabRequestReview.view.xml b/mcc/resources/views/rabRequestReview.view.xml
new file mode 100644
index 000000000..7e62677d0
--- /dev/null
+++ b/mcc/resources/views/rabRequestReview.view.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mcc/resources/views/requestLogin.html b/mcc/resources/views/requestLogin.html
index 6c3b1fb7f..90c7565ad 100644
--- a/mcc/resources/views/requestLogin.html
+++ b/mcc/resources/views/requestLogin.html
@@ -42,7 +42,7 @@
},{
xtype: 'textfield',
labelWidth: 130,
- fieldLabel: 'Title',
+ fieldLabel: 'Job Title',
name: 'title',
width: 500,
allowBlank: false
diff --git a/mcc/resources/web/mcc/Utils.js b/mcc/resources/web/mcc/Utils.js
index ca9b72c64..95636656b 100644
--- a/mcc/resources/web/mcc/Utils.js
+++ b/mcc/resources/web/mcc/Utils.js
@@ -12,8 +12,18 @@ MCC.Utils = new function() {
var requiredProps = requiredProps || ['MCCContainer', 'MCCRequestContainer'];
var missingProps = [];
for (var i=0;i
0){
@@ -23,6 +33,24 @@ MCC.Utils = new function() {
}
return ctx;
+ },
+
+ isRequestAdmin: function(){
+ var ctx = LABKEY.getModuleContext('mcc');
+
+ return !!ctx.hasRequestAdminPermission;
+ },
+
+ hasRabPermission: function(){
+ var ctx = LABKEY.getModuleContext('mcc');
+
+ return !!ctx.hasRabPermission;
+ },
+
+ hasFinalDecisionPermission: function(){
+ var ctx = LABKEY.getModuleContext('mcc');
+
+ return !!ctx.hasFinalDecisionPermission;
}
}
};
\ No newline at end of file
diff --git a/mcc/resources/web/mcc/img/mccRequest.png b/mcc/resources/web/mcc/img/mccRequest.png
new file mode 100644
index 000000000..adc667f00
Binary files /dev/null and b/mcc/resources/web/mcc/img/mccRequest.png differ
diff --git a/mcc/resources/web/mcc/window/ReviewerNotifyWindow.js b/mcc/resources/web/mcc/window/ReviewerNotifyWindow.js
new file mode 100644
index 000000000..52505da8d
--- /dev/null
+++ b/mcc/resources/web/mcc/window/ReviewerNotifyWindow.js
@@ -0,0 +1,63 @@
+Ext4.define('MCC.window.ReviewerNotifyWindow', {
+ extend: 'Ext.window.Window',
+
+ statics: {
+ buttonHandler: function (dataRegionName) {
+ var dataRegion = LABKEY.DataRegions[dataRegionName];
+ var checked = dataRegion.getChecked();
+ if (!checked || !checked.length) {
+ Ext4.Msg.alert('Error', 'No records selected');
+ return;
+ }
+
+ Ext4.create('MCC.window.ReviewerNotifyWindow', {
+ dataRegionName: dataRegionName,
+ rowIds: checked
+ }).show();
+ }
+ },
+
+ initComponent: function(){
+ Ext4.apply(this, {
+ bodyStyle: 'padding: 5px;',
+ width: 500,
+ modal: true,
+ title: 'Send Reviewer Notifications',
+ items: [{
+ html: 'This will send a notification email to the reviewers assigned to the ' + this.rowIds.length + ' selected records.',
+ border: false,
+ style: 'padding-bottom: 10px;'
+ }],
+ buttons: [{
+ text: 'Submit',
+ handler: this.onSubmit,
+ scope: this
+ },{
+ text: 'Cancel',
+ handler: function(btn){
+ btn.up('window').close();
+ }
+ }]
+ });
+
+ this.callParent(arguments);
+ },
+
+ onSubmit: function(btn){
+ Ext4.Msg.wait('Loading...');
+
+ LABKEY.Ajax.request({
+ url: LABKEY.ActionURL.buildURL('mcc', 'notifyReviewers'),
+ scope: this,
+ failure: LDK.Utils.getErrorCallback(),
+ success: LABKEY.Utils.getCallbackWrapper(function (results) {
+ Ext4.Msg.hide();
+ Ext4.Msg.alert('Success', 'Notification Emails Sent');
+ btn.up('window').close();
+ }, this),
+ jsonData: {
+ rowIds: this.rowIds
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/mcc/src/client/AnimalRequest/animal-request.tsx b/mcc/src/client/AnimalRequest/animal-request.tsx
index 3e76fa3a8..309fa4d05 100644
--- a/mcc/src/client/AnimalRequest/animal-request.tsx
+++ b/mcc/src/client/AnimalRequest/animal-request.tsx
@@ -1,135 +1,118 @@
-import React, { useState, FormEvent } from 'react'
-import { Query, ActionURL, Filter, getServerContext } from '@labkey/api';
-import { nanoid } from 'nanoid'
-
-import Tooltip from './tooltip'
-import Title from './title'
-import Input from './input'
-import Select from './select'
-import CoInvestigators from './co-investigators'
-import TextArea from './text-area'
-import YesNoRadio from './yes-no-radio'
-import AnimalBreeding from './animal-breeding'
-import IACUCProtocol from './iacuc-protocol'
-import Funding from './funding'
-import ResearchArea from './research-area'
-import AnimalCohorts from './animal-cohort'
-import Button from './button'
-import SavingOverlay from './saving-overlay'
-import ErrorMessageHandler from './error-message-handler'
+import React, { FormEvent, useState } from 'react';
+import { ActionURL, getServerContext, Query } from '@labkey/api';
+import {
+ AnimalCohort,
+ AnimalRequestModel,
+ CoInvestigatorModel,
+ queryRequestInformation
+} from '../components/RequestUtils';
+
+import Tooltip from './components/tooltip';
+import Title from './components/title';
+import Input from './components/input';
+import Select from './components/select';
+import CoInvestigators from './components/co-investigators';
+import TextArea from './components/text-area';
+import YesNoRadio from './components/yes-no-radio';
+import AnimalBreeding from './components/animal-breeding';
+import IACUCProtocol from './components/iacuc-protocol';
+import Funding from './components/funding';
+import AnimalCohorts from './components/animal-cohort';
+import Button from './components/button';
+import SavingOverlay from './saving-overlay';
+import ErrorMessageHandler from './components/error-message-handler';
import {
+ animalWellfarePlaceholder,
+ certificationLabel,
+ collaborationsPlaceholder,
earlyInvestigatorTooltip,
- experimentalRationalePlaceholder,
- methodsProposedPlaceholder, collaborationsPlaceholder,
- animalWellfarePlaceholder, signingOfficialTooltip,
- certificationLabel, existingMarmosetColonyOptions,
+ existingMarmosetColonyOptions,
existingNHPFacilityOptions,
- researchUseStatementTooltip
-} from './values'
-
+ experimentalRationalePlaceholder,
+ institutionTypeOptions,
+ methodsProposedPlaceholder,
+ signingOfficialTooltip
+} from './components/values';
export function AnimalRequest() {
const requestId = (new URLSearchParams(window.location.search)).get("requestId")
+
const [isSubmitting, setIsSubmitting] = useState(false)
const [displayOverlay, setDisplayOverlay] = useState(false)
- const [stateRollbackOnFailure, setStateRollbackOnFailure] = useState({
- "returned": false,
- "data": {"status": "draft",
- middleinitial: undefined,
- lastname: undefined,
- firstname: undefined,
- isprincipalinvestigator: undefined,
- institutionname: undefined,
- institutioncity: undefined,
- institutionstate: undefined,
- institutioncountry: undefined,
- officiallastname: undefined,
- officialfirstname: undefined,
- officialemail: undefined,
- experimentalrationale: undefined,
- methodsproposed: undefined,
- collaborations: undefined,
- existingmarmosetcolony: undefined,
- existingnhpfacilities: undefined,
- rowid: undefined,
- certify: false,
- animalwelfare: undefined,
- vetlastname: undefined,
- vetemail: undefined,
- vetfirstname: undefined
+ const [requestData, setRequestData] = useState(null)
+ const [stateRollbackOnFailure, setStateRollbackOnFailure] = useState(requestData?.request?.status)
+
+ const [ deletedCohortRecords, setDeletedCohortRecords ] = useState(new Set())
+ const [ deletedCoIRecords, setDeletedCoIRecords ] = useState(new Set())
+
+ function onAddCohort() {
+ requestData.cohorts.push(new AnimalCohort())
+ setRequestData({...requestData})
+ }
+
+ function onRemoveCohort(cohort: AnimalCohort) {
+ // NOTE: dont allow removing all cohorts
+ if (requestData.cohorts.length > 1) {
+ requestData.cohorts = [...requestData.cohorts.filter(item => item.uuid !== cohort.uuid)]
+
+ if (cohort.rowid) {
+ deletedCohortRecords.add(cohort.rowid)
+ setDeletedCohortRecords(new Set(deletedCohortRecords))
+ }
+
+ setRequestData({...requestData})
}
- })
-
- // On submit, state is managed by the FormData object in handleSubmit. These hooks are only used to propagate values
- // from the database into the form via fillForm if there is a requestId
- const [isFormQueried, setIsFormQueried] = useState(false)
- const [animalRequests, setAnimalRequests] = useState({
- "returned": false,
- "data": {"status": "draft",
- middleinitial: undefined,
- lastname: undefined,
- firstname: undefined,
- isprincipalinvestigator: undefined,
- institutionname: undefined,
- institutioncity: undefined,
- institutionstate: undefined,
- institutioncountry: undefined,
- officiallastname: undefined,
- officialfirstname: undefined,
- officialemail: undefined,
- experimentalrationale: undefined,
- methodsproposed: undefined,
- collaborations: undefined,
- existingmarmosetcolony: undefined,
- existingnhpfacilities: undefined,
- rowid: undefined,
- certify: false,
- animalwelfare: undefined,
- vetlastname: undefined,
- vetemail: undefined,
- vetfirstname: undefined
+ }
+
+ function onAddInvestigator() {
+ requestData.coinvestigators.push(new CoInvestigatorModel())
+ setRequestData({...requestData})
+ }
+
+ function onRemoveCoInvestigator(coInvestigator: CoInvestigatorModel) {
+ if (requestData.coinvestigators.length) {
+ requestData.coinvestigators = [...requestData.coinvestigators.filter(item => item.uuid !== coInvestigator.uuid)]
+
+ if (coInvestigator.rowid) {
+ deletedCoIRecords.add(coInvestigator.rowid)
+ setDeletedCoIRecords(new Set(deletedCoIRecords))
+ }
+
+ setRequestData({...requestData})
}
- })
- const [coinvestigators, setCoinvestigators] = useState({
- "returned": false,
- "data": []
- })
- const [animalCohorts, setAnimalCohorts] = useState({
- "returned": false,
- "data": [{uuid: nanoid(), rowid: undefined, othercharacteristics: undefined}]
- })
-
+ }
+
+ if (!requestData) {
+ queryRequestInformation(requestId, handleFailure).then((model) => {
+ setRequestData(model)
+ setStateRollbackOnFailure(requestData?.request.status)
+ })
+ }
+
+ if (!requestData || !requestData?.dataLoaded) {
+ return (
+
+ )
+ }
+ else if (requestData.dataLoaded === true && !requestData.request) {
+ return(There is no request with id: {requestId}
)
+ }
function handleFailure(response) {
+ console.error(response.exception)
+ console.error(response)
alert(response.exception) //this is probably what you want to show. An example would be to submit data with a long value for middle initial (>14 characters)
setDisplayOverlay(false)
- setAnimalRequests({
- "returned": stateRollbackOnFailure.returned,
- "data": { ...stateRollbackOnFailure.data, status: stateRollbackOnFailure.data.status }
- })
+
+ requestData.request.status = stateRollbackOnFailure
+ setRequestData({...requestData})
}
-
- function getRequired() {
- switch (animalRequests.data.status) {
- case "draft":
- return false
- case "submitting":
- return true
- case "submitted":
- return false
- case "approving":
- return true
- case "under-review":
- return true
- case "approving-final":
- return true
- case "rejecting":
- return true
- default:
- return false
- }
+ function doEnforceRequiredFields() {
+ return isSubmitting || requestData.request.status !== "Draft"
}
// The general idea is that users with MCCRequestAdminPermission can edit all states.
@@ -140,215 +123,155 @@ export function AnimalRequest() {
return true
}
- if (!animalRequests.data.status) {
+ if (!requestData.request.status) {
return true
}
- return "draft" === animalRequests.data.status || "submitting" === animalRequests.data.status
+ return "Draft" === requestData.request.status
}
- function handleNextStateSubmitButton() {
- setIsSubmitting(true);
-
- if (animalRequests.data.status === "draft") {
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"submitting" }
- });
- } else if (animalRequests.data.status === "submitted") {
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"approving" }
- });
- } else if (animalRequests.data.status === "under-review") {
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"approving-final" }
- });
+ function handleSubmitButton(e, isSubmitting) {
+ setIsSubmitting(isSubmitting);
+
+ if (!isSubmitting) {
+ // Use this to reset each field's error state
+ e.target.form.querySelectorAll('input, select, textarea').forEach(function(e){
+ e.checkValidity()
+ })
}
}
function getSubmitButtonText() {
- switch (animalRequests.data.status) {
- case "draft":
- return "Submit"
- case "submitting":
+ switch (requestData.request.status) {
+ case "Draft":
return "Submit"
- case "submitted":
- return "Approve Request"
- case "approving":
- return "Approve Request"
- case "under-review":
- return "Approve"
- case "approving-final":
- return "Approve"
- case "rejecting":
- return "Approve"
default:
- "Submit"
+ return "Update Request"
}
}
-
- function handleNextStateSaveButton() {
- setIsSubmitting(false)
-
- if (animalRequests.data.status === "submitting") {
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"draft" }
- });
- } else if (animalRequests.data.status === "approving") {
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"submitted" }
- });
- } else if (animalRequests.data.status === "under-review") {
- setIsSubmitting(true)
- setAnimalRequests({
- "returned": true,
- "data": { ...animalRequests.data, status:"rejecting" }
- });
- }
- }
-
function getSaveButtonText() {
- switch (animalRequests.data.status) {
- case "draft":
- return "Save"
- case "submitting":
- return "Save"
- case "submitted":
- return "Save"
- case "approving":
- return "Save"
- case "under-review":
- return "Reject"
- case "approving-final":
- return "Reject"
- case "rejecting":
- return "Reject"
- default:
- "Save"
- }
+ return "Save"
}
-
- function get_coinvestigator_commands(data, objectId) {
+ function getCoinvestigatorCommands(data) {
let commands = []
- let i = 0
- if(requestId && coinvestigators.data.length > 0) {
- for(const coinvestigator of coinvestigators.data) {
+ if (requestData.coinvestigators.length) {
+ requestData.coinvestigators.forEach((rec, i) => {
commands.push({
- command: "delete",
+ command: rec.rowid ? "update" : "insert",
schemaName: "mcc",
queryName: "coinvestigators",
rows: [{
- "rowid": coinvestigator.rowid,
+ "rowid": rec.rowid,
+ "requestId": requestData.request.objectid,
+ "lastname": data.get("coinvestigators-" + i + "-lastName"),
+ "firstname": data.get("coinvestigators-" + i + "-firstName"),
+ "middleinitial": data.get("coinvestigators-" + i + "-middleInitial"),
+ "institutionname": data.get("coinvestigators-" + i + "-institution"),
}]
})
- }
+ })
}
- while(data.get("coinvestigators-" + i + "-lastName") || data.get("coinvestigators-" + i + "-firstName") ||
- data.get("coinvestigators-" + i + "-middleInitial") || data.get("coinvestigators-" + i + "-institution")) {
- commands.push({
- command: "insert",
- schemaName: "mcc",
- queryName: "coinvestigators",
- rows: [{
- "requestId": objectId,
- "lastname": data.get("coinvestigators-" + i + "-lastName"),
- "firstname": data.get("coinvestigators-" + i + "-firstName"),
- "middleinitial": data.get("coinvestigators-" + i + "-middleInitial"),
- "institutionname": data.get("coinvestigators-" + i + "-institution"),
- }]
+ if (deletedCoIRecords.size) {
+ deletedCoIRecords.forEach(rowId => {
+ commands.push({
+ command: "delete",
+ schemaName: "mcc",
+ queryName: "coinvestigators",
+ rows: [{
+ "rowid": rowId,
+ }]
+ })
})
-
- i++
}
return commands
}
-
- function get_animal_cohort_commands(data, objectId) {
+ function getAnimalCohortCommands(data) {
let commands = []
- let i = 0
- if(requestId && animalCohorts.data.length > 0) {
- for(const cohort of animalCohorts.data) {
+ if (requestData.cohorts.length) {
+ requestData.cohorts.forEach((rec, i) => {
commands.push({
- command: "delete",
+ command: rec.rowid ? "update" : "insert",
schemaName: "mcc",
queryName: "requestcohorts",
rows: [{
- "rowid": cohort.rowid,
+ "rowid": rec.rowid,
+ "requestId": requestData.request.objectid,
+ "numberofanimals": data.get("animal-cohorts-" + i + "-numberofanimals"),
+ "sex": data.get("animal-cohorts-" + i + "-sex"),
+ "othercharacteristics": data.get("animal-cohorts-" + i + "-othercharacteristics"),
}]
})
- }
+ })
}
- while(data.get("animal-cohorts-" + i + "-numberofanimals") || data.get("animal-cohorts-" + i + "-sex") ||
- data.get("animal-cohorts-" + i + "-othercharacteristics")) {
- commands.push({
- command: "insert",
- schemaName: "mcc",
- queryName: "requestcohorts",
- rows: [{
- "requestId": objectId,
- "numberofanimals": data.get("animal-cohorts-" + i + "-numberofanimals"),
- "sex": data.get("animal-cohorts-" + i + "-sex"),
- "othercharacteristics": data.get("animal-cohorts-" + i + "-othercharacteristics"),
- }]
+ if (deletedCohortRecords.size) {
+ deletedCohortRecords.forEach(rowId => {
+ commands.push({
+ command: "delete",
+ schemaName: "mcc",
+ queryName: "requestcohorts",
+ rows: [{
+ "rowid": rowId,
+ }]
+ })
})
-
- i++
}
return commands
}
-
function handleSubmit(e: FormEvent) {
e.preventDefault()
setDisplayOverlay(true)
- setStateRollbackOnFailure({
- "returned": animalRequests.returned,
- "data": { ...animalRequests.data, status: animalRequests.data.status }
- })
- if(animalRequests.data.status === "submitting") {
- animalRequests.data.status = "submitted"
- } else if(animalRequests.data.status === "approving") {
- animalRequests.data.status = "under-review"
- } else if(animalRequests.data.status === "approving-final") {
- animalRequests.data.status = "approved"
- } else if(animalRequests.data.status === "rejecting") {
- animalRequests.data.status = "rejected"
+ // NOTE: the idea is that when the user hits 'submit', this changes Draft to Submitted.
+ // Any other action preserved the status as-is
+ if (isSubmitting) {
+ setStateRollbackOnFailure(requestData.request.status)
+ if (requestData.request.status === 'Draft') {
+ requestData.request.status = 'Submitted'
+
+ setRequestData({...requestData})
+ }
}
+
+ const el = e.currentTarget as HTMLFormElement
+ const data = new FormData(el)
+ el.querySelectorAll('select[multiple]').forEach(function(x){
+ data.set(x.id, Array.from(x.selectedOptions, option => option.value).join(','))
+ })
- const data = new FormData(e.currentTarget as HTMLFormElement)
+ if (!requestData.request.status) {
+ console.error('Request being submitted without a status!')
+ }
- const objectId = requestId || nanoid()
- let coinvestigatorCommands = get_coinvestigator_commands(data, objectId)
- let cohortCommands = get_animal_cohort_commands(data, objectId)
+ if (!requestData.request.objectid) {
+ console.error('Request being submitted without an objectId!')
+ }
- let rowId = requestId ? {"rowid": animalRequests.data.rowid} : {}
+ // NOTE: use a proper v4 UUID so this is compatible with the sqlserver ENTITYID datatype
+ let coinvestigatorCommands = getCoinvestigatorCommands(data)
+ let cohortCommands = getAnimalCohortCommands(data)
Query.saveRows({
- commands: [
- {
- command: requestId ? "update" : "insert",
+ commands: [{
+ command: requestData.request.rowid ? "update" : "insert",
schemaName: "mcc",
queryName: "animalrequests",
rows: [{
- "objectId": objectId,
+ "rowid": requestData.request.rowid,
+ "objectId": requestData.request.objectid,
"lastname": data.get("investigator-last-name"),
"firstname": data.get("investigator-first-name"),
"middleinitial": data.get("investigator-middle-initial"),
- "isprincipalinvestigator": data.get("is-principal-investigator"),
+ "earlystageinvestigator": data.get("is-early-stage-investigator"),
"institutionname": data.get("institution-name"),
"institutioncity": data.get("institution-city"),
"institutionstate": data.get("institution-state"),
@@ -363,12 +286,10 @@ export function AnimalRequest() {
"collaborations": data.get("collaborations"),
"isbreedinganimals": data.get("animal-breeding-is-planning-to-breed-animals"),
"breedingpurpose": data.get("animal-breeding-purpose"),
- "researcharea": data.get("research-area"),
- "otherjustification": data.get("research-area-other-specify"),
"existingmarmosetcolony": data.get("existing-marmoset-colony"),
"existingnhpfacilities": data.get("existing-nhp-facilities"),
"animalwelfare": data.get("animal-welfare"),
- "certify": data.get("certify"),
+ "certify": !!data.get("certify"),
"vetlastname": data.get("vet-last-name"),
"vetfirstname": data.get("vet-first-name"),
"vetemail": data.get("vet-email"),
@@ -376,352 +297,356 @@ export function AnimalRequest() {
"iacucprotocol": data.get("iacuc-protocol"),
"grantnumber" : data.get("funding-grant-number"),
"applicationduedate": data.get("funding-application-due-date"),
- "status": animalRequests.data.status,
- ...rowId
+ "status": requestData.request.status,
}]
},
...coinvestigatorCommands,
...cohortCommands
],
success: function(response) {
- window.location.href = ActionURL.buildURL('mcc', 'mccRequests.view')
- },
- failure: handleFailure
- })
- }
+ // set RowIDs:
+ if (response.result[0].queryName !== 'animalrequests') {
+ console.error('The first command was not animal requests! This is not expected.')
+ }
+
+ const rowId = response.result[0].rows[0].rowid
+ if (!rowId) {
+ console.error('No rowId found for the animalrequest row after save. This is not expected.')
+ }
+ else {
+ requestData.request.rowid = rowId
+ }
+
+ if (response.result[0].rows[0].rowid.status) {
+ requestData.request.status = response.result[0].rows[0].rowid.status
+ }
+ else {
+ console.error('Status was null for the animalrequest row after save. This is not expected.')
+ }
+
+ response.result.filter(x => x.command !== 'delete' && x.queryName === 'requestcohorts').forEach((x, idx) => {
+ if (requestData.cohorts.length < idx) {
+ console.error('There are more cohort saveRows commands than client-side records')
+ return
+ }
+
+ if (x.rows.length != 1) {
+ console.error('Expected a single row per cohort saveRows command, found: ' + x.rows.length)
+ return
+ }
+
+ if (requestData.cohorts[idx].rowid && requestData.cohorts[idx].rowid != x.rows[0].rowid) {
+ console.error('Cohort row has existing rowId but doesnt match the server: ' + requestData.cohorts[idx].rowid + ' / ' + x.rows[0].rowid)
+ return
+ }
+
+ requestData.cohorts[idx].rowid = x.rows[0].rowid
+ })
+ response.result.filter(x => x.command !== 'delete' && x.queryName === 'coinvestigators').forEach((x, idx) => {
+ if (requestData.coinvestigators.length < idx) {
+ console.error('There are more coinvestigators saveRows commands than client-side records')
+ return
+ }
- function fillForm() {
- setIsFormQueried(true)
-
- Query.selectRows({
- schemaName: "mcc",
- queryName: "animalrequests",
- columns: [
- "rowid",
- "objectId",
- "lastname",
- "firstname",
- "middleinitial",
- "isprincipalinvestigator",
- "institutionname",
- "institutioncity",
- "institutionstate",
- "institutioncountry",
- "institutiontype",
- "officiallastname",
- "officialfirstname",
- "officialemail",
- "fundingsource",
- "experimentalrationale",
- "methodsproposed",
- "collaborations",
- "isbreedinganimals",
- "breedingpurpose",
- "researcharea",
- "otherjustification",
- "existingmarmosetcolony",
- "existingnhpfacilities",
- "animalwelfare",
- "certify",
- "vetlastname",
- "vetfirstname",
- "vetemail",
- "iacucapproval",
- "iacucprotocol",
- "grantnumber",
- "applicationduedate",
- "status"
- ],
- filterArray: [
- Filter.create('objectId', requestId)
- ],
- success: function(resp) {
- let returnedData = resp.rows[0]
+ if (x.rows.length != 1) {
+ console.error('Expected a single row per coinvestigators saveRows command, found: ' + x.rows.length)
+ return
+ }
- setAnimalRequests({
- "returned": true,
- "data": returnedData
- })
- },
- failure: handleFailure
- })
+ if (requestData.coinvestigators[idx].rowid && requestData.coinvestigators[idx].rowid != x.rows[0].rowid) {
+ console.error('Coinvestigators row has existing rowId but doesnt match the server: ' + requestData.coinvestigators[idx].rowid + ' / ' + x.rows[0].rowid)
+ return
+ }
+ requestData.coinvestigators[idx].rowid = x.rows[0].rowid
+ })
- Query.selectRows({
- schemaName: "mcc",
- queryName: "coinvestigators",
- columns: [
- "rowid",
- "requestId",
- "lastname",
- "firstname",
- "middleinitial",
- "institutionname",
- ],
- filterArray: [
- Filter.create('requestId', requestId)
- ],
- success: function(resp) {
- let returnedData = resp.rows
- setCoinvestigators({
- "returned": true,
- "data": returnedData
+ response.result.filter(x => x.command === 'delete').forEach((x, idx) => {
+ if (x.rows.length != 1) {
+ console.error('Expected a single row per saveRows delete command, found: ' + x.rows.length)
+ return
+ }
+
+ const rowId = x.rows[0].rowid
+ if (x.queryName === 'requestcohorts') {
+ deletedCohortRecords.delete(rowId)
+ setDeletedCohortRecords(new Set(deletedCohortRecords))
+ }
+ else if (x.queryName === 'coinvestigators') {
+ deletedCoIRecords.delete(rowId)
+ setDeletedCoIRecords(new Set(deletedCoIRecords))
+ }
})
- },
- failure: handleFailure
- })
+ if (deletedCoIRecords.size) {
+ console.log('there are still records in deletedCoIRecords: ' + deletedCoIRecords.size)
+ }
- Query.selectRows({
- schemaName: "mcc",
- queryName: "requestcohorts",
- columns: [
- "rowid",
- "requestId",
- "numberofanimals",
- "sex",
- "othercharacteristics",
- ],
- filterArray: [
- Filter.create('requestId', requestId)
- ],
- success: function(resp) {
- let returnedData = resp.rows
- setAnimalCohorts({
- "returned": true,
- "data": returnedData
+ if (deletedCohortRecords.size) {
+ console.log('there are still records in deletedCohortRecords: ' + deletedCohortRecords.size)
+ }
+
+ setRequestData({...requestData})
+ setStateRollbackOnFailure(requestData.request.status)
+
+ if (!requestData.request.rowid) {
+ console.error("Missing request rowid after save")
+ }
+
+ requestData.cohorts.forEach(x => {
+ if (!x.rowid) {
+ console.error("Missing cohort rowid after save")
+ }
+ })
+
+ requestData.coinvestigators.forEach(x => {
+ if (!x.rowid) {
+ console.error("Missing coinvestigator rowid after save")
+ }
})
+
+ setDisplayOverlay(false)
+ if (isSubmitting) {
+ const returnURL = (new URLSearchParams(window.location.search)).get("requestId")
+ let dest = ActionURL.buildURL('mcc', 'mccRequests.view')
+ if (returnURL) {
+ try {
+ dest = new URL(returnURL).href
+ }
+ catch (e) {
+
+ }
+ }
+
+ window.location.href = dest
+ }
},
failure: handleFailure
})
-
}
+ return (
+ <>
+