Skip to content

Commit 189b9a4

Browse files
authored
New Animal Dev - multiple animal inserts (#278)
* Hamsters are acquired in bulk - changes made to Acquisition Panel to allow user to specify the number of hamsters to be added. * 1. Changes need to be made to Acquisition Panel, NewAnimalPage, and SaveModal to allow user to specify the number of hamsters to be added. 2. Updated PotentialDams and PotentialSires queries to include gestational age for hamsters * Fixed formatting * Updated snapshots * 1. Automated testing and styling changes for hamster acquisition changes. 2. Updated snapshots
1 parent f9539d3 commit 189b9a4

22 files changed

Lines changed: 1017 additions & 113 deletions

File tree

snprc_ehr/resources/queries/study/PotentialDams.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ SELECT d.id as Dam,
77
d.birth as BirthDate,
88
d.id.age.ageInYears as Age
99

10-
1110
FROM study.demographics AS d
1211
INNER JOIN (
1312
SELECT "min" AS minAdultAge, species, gender, label
@@ -26,8 +25,10 @@ FROM study.demographics AS d
2625
UNION ALL
2726
SELECT 223 as gestation, 'PT' as species
2827
UNION ALL
28+
SELECT 22 as gestation, 'MA' as species
29+
UNION ALL
2930
SELECT 185 as gestation, 'O' as species
30-
) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT') then d.species.arc_species_code ELSE 'O' END = y.species
31+
) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT','MA') then d.species.arc_species_code ELSE 'O' END = y.species
3132

3233
INNER JOIN study.acq_disp as ad on d.id = ad.id
3334

snprc_ehr/resources/queries/study/PotentialSires.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ FROM study.demographics AS d
2525
UNION ALL
2626
SELECT 223 as gestation, 'PT' as species
2727
UNION ALL
28+
SELECT 22 as gestation, 'MA' as species
29+
UNION ALL
2830
SELECT 185 as gestation, 'O' as species
29-
) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT') then d.species.arc_species_code ELSE 'O' END = y.species
31+
) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT', 'MA') then d.species.arc_species_code ELSE 'O' END = y.species
3032

3133
INNER JOIN study.acq_disp as ad on d.id = ad.id
3234

snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ export default class NewAnimalPage extends React.Component {
186186
}
187187
};
188188

189+
handleNumAnimalChange = value => {
190+
this.setState(
191+
prevState => ({
192+
...prevState,
193+
numAnimals: value
194+
})
195+
)
196+
}
197+
189198
handleDataChange = (property, value) => {
190199
this.setState(
191200
prevState => ({
@@ -294,46 +303,53 @@ export default class NewAnimalPage extends React.Component {
294303
onSaveClick = () => {
295304
console.log('Saving...')
296305

297-
// run async save then dismiss modal
298-
uploadAnimalData(this.state.newAnimalData)
299-
.then(data => {
300-
if (data.success === true) {
301-
this.setState(
302-
prevState => ({
306+
// Multiple animals can be added for hamsters, which are often acquired and assigned in bulk
307+
const numAnimals = this.state.numAnimals ? this.state.numAnimals : 1
308+
309+
for (let i = 0; i < numAnimals; i++) {
310+
// run async save then dismiss modal
311+
uploadAnimalData(this.state.newAnimalData)
312+
.then(data => {
313+
if (data.success === true) {
314+
this.setState(
315+
prevState => ({
316+
...prevState,
317+
newAnimalData: {
318+
...prevState.newAnimalData,
319+
id: data.Id,
320+
},
321+
showSaveModal: false,
322+
summaryData: [
323+
...prevState.summaryData,
324+
{ ...prevState.newAnimalData, id: data.Id },
325+
],
326+
}),
327+
this.handleSaveReset()
328+
)
329+
} else {
330+
this.setState(prevState => ({
303331
...prevState,
304-
newAnimalData: {
305-
...prevState.newAnimalData,
306-
id: data.Id,
307-
},
332+
errorMessage: data.message,
308333
showSaveModal: false,
309-
summaryData: [
310-
...prevState.summaryData,
311-
{ ...prevState.newAnimalData, id: data.Id },
312-
],
313-
}),
314-
this.handleSaveReset()
315-
)
316-
} else {
334+
}))
335+
}
336+
})
337+
.catch(error => {
317338
this.setState(prevState => ({
318339
...prevState,
319-
errorMessage: data.message,
340+
errorMessage: error.exception,
320341
showSaveModal: false,
321342
}))
322-
}
323-
})
324-
.catch(error => {
325-
this.setState(prevState => ({
326-
...prevState,
327-
errorMessage: error.exception,
328-
showSaveModal: false,
329-
}))
330-
})
343+
})
344+
}
345+
331346
};
332347

333348
handleSaveReset = () => {
334349
this.setState(prevState => ({
335350
...prevState,
336351
currentPanel: 1,
352+
numAnimals: undefined,
337353
newAnimalData: {
338354
...prevState.newAnimalData,
339355
...(prevState.selectedOption === 'Acquisition' && {
@@ -349,7 +365,7 @@ export default class NewAnimalPage extends React.Component {
349365
cage: prevState.newAnimalData.cage
350366

351367
})
352-
|| {
368+
|| {
353369
sire: undefined,
354370
dam: undefined,
355371
cage: { value: undefined }
@@ -489,9 +505,11 @@ export default class NewAnimalPage extends React.Component {
489505
this.state.acquisitionTypeList
490506
}
491507
disabled={ this.disableFirstPanel() }
492-
handleDataChange={ this.handleDataChange }
508+
numAnimals={ this.state.numAnimals }
493509
newAnimalData={ this.state.newAnimalData }
510+
handleDataChange={ this.handleDataChange }
494511
preventNext={ this.preventNext }
512+
handleNumAnimalChange={ this.handleNumAnimalChange }
495513
/>
496514
</div>
497515
</div>
@@ -685,6 +703,7 @@ export default class NewAnimalPage extends React.Component {
685703
{/* Save Modal */ }
686704
<SaveModal
687705
newAnimalData={ this.state.newAnimalData }
706+
numAnimals={ this.state.numAnimals }
688707
onCloseClick={ this.onCloseClick }
689708
onSaveClick={ this.onSaveClick }
690709
show={ this.state.showSaveModal }

snprc_ehr/src/client/NewAnimalPage/components/AcquisitionPanel.jsx

Lines changed: 118 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,134 @@ import Select from 'react-select'
33
import moment from 'moment'
44
import WrappedDatePicker from '../../Shared/components/WrappedDatePicker'
55
import InfoPanel from '../../Shared/components/InfoPanel'
6+
import { validateNumAnimals } from '../services/validation'
7+
import constants from '../constants/index'
68

79
export default class AcquisitionPanel extends React.Component {
8-
componentDidMount = () => {
9-
this.props.preventNext() // prevent/Allow Next button
10-
}
10+
state = {
11+
errorMessage: undefined,
12+
infoMessage: undefined
13+
}
14+
15+
componentDidMount = () => {
16+
this.props.preventNext() // prevent/Allow Next button
17+
}
18+
19+
handleAcquisitionChange = option => {
20+
this.props.handleDataChange('acquisitionType', option)
21+
}
22+
23+
handleAcquisitionDateChange = date => {
24+
this.props.handleDataChange('acqDate', { date: moment(date) })
25+
}
1126

12-
handleAcquisitionChange = option => {
13-
this.props.handleDataChange('acquisitionType', option)
27+
handleNumAnimalChange = e => {
28+
const numAnimals = e.target.value
29+
30+
const errorMessage = validateNumAnimals(numAnimals)
31+
if (errorMessage === undefined) {
32+
this.props.handleNumAnimalChange(numAnimals)
1433
}
1534

16-
handleAcquisitionDateChange = date => {
17-
this.props.handleDataChange('acqDate', { date: moment(date) })
35+
this.setState(prevState => (
36+
{
37+
...prevState,
38+
errorMessage
39+
}
40+
))
41+
}
42+
43+
isInteger = e => {
44+
const i = e.key // which ? e.which : e.keyCode;
45+
const isInteger = (i >= 0 && i <= 9)
46+
if (!isInteger) {
47+
e.preventDefault()
1848
}
1949

20-
render() {
21-
const { acquisitionType, acqDate } = this.props.newAnimalData
50+
return isInteger
51+
}
52+
53+
handlePaste = e => {
54+
e.preventDefault()
55+
}
2256

23-
return (
24-
<>
25-
<div className="wizard-panel__rows">
26-
<div className="wizard-panel__row">
27-
<div className="wizard-panel__col">
28-
<WrappedDatePicker
29-
dateFormat="Pp"
57+
render() {
58+
const { acquisitionType, acqDate, species } = this.props.newAnimalData
59+
const numAnimals = this.props.numAnimals
60+
61+
return (
62+
<>
63+
<div className="wizard-panel__rows">
64+
<div className="wizard-panel__row">
65+
<div className="wizard-panel__col">
66+
<WrappedDatePicker
67+
dateFormat="Pp"
68+
disabled={ this.props.disabled }
69+
id="acquisition-datepicker"
70+
label="Acquisition Date"
71+
maxDate={ moment().toDate() }
72+
onChange={ this.handleAcquisitionDateChange }
73+
onSelect={ this.handleAcquisitionDateChange }
74+
selected={ acqDate.date.toDate() }
75+
showTimeSelect
76+
timeFormat="p"
77+
timeIntervals={ 30 }
78+
todayButton="Today"
79+
/>
80+
</div>
81+
</div>
82+
<div className="wizard-panel__row">
83+
<div className="wizard-panel__col">
84+
<label className="field-label">Acquisition Code</label>
85+
<Select
86+
autoFocus
87+
className="shared-dropdown"
88+
classNamePrefix="shared-select"
89+
id="acquisition-select"
90+
isClearable
91+
isDisabled={ this.props.disabled }
92+
isLoading={ this.props.acquisitionTypeList.length === 0 }
93+
onChange={ this.handleAcquisitionChange }
94+
options={ this.props.acquisitionTypeList }
95+
placeholder="Select Acquisition Type"
96+
value={ acquisitionType || null }
97+
/>
98+
</div>
99+
</div>
100+
{ species && species.arcSpeciesCode === 'MA' && (
101+
<div className="wizard-panel__row">
102+
<div className="wizard-panel__col">
103+
<label className="field-label">Number of Animals</label>
104+
105+
<input
106+
className="acq-input"
107+
defaultValue={ numAnimals ? numAnimals : 1 }
30108
disabled={ this.props.disabled }
31-
id="acquisition-datepicker"
32-
label="Acquisition Date"
33-
maxDate={ moment().toDate() }
34-
onChange={ this.handleAcquisitionDateChange }
35-
onSelect={ this.handleAcquisitionDateChange }
36-
selected={ acqDate.date.toDate() }
37-
showTimeSelect
38-
timeFormat="p"
39-
timeIntervals={ 30 }
40-
todayButton="Today"
109+
onKeyPress={ this.isInteger }
110+
onPasteCapture={ this.handlePaste }
111+
type="text"
112+
onChange={ this.handleNumAnimalChange }
113+
max="100"
114+
min="1"
115+
type="number"
41116
/>
42-
</div>
117+
43118
</div>
44-
<div className="wizard-panel__row">
45-
<div className="wizard-panel__col">
46-
<label className="field-label">Acquisition Code</label>
47-
<Select
48-
autoFocus
49-
className="shared-dropdown"
50-
classNamePrefix="shared-select"
51-
id="acquisition-select"
52-
isClearable
53-
isDisabled={ this.props.disabled }
54-
isLoading={ this.props.acquisitionTypeList.length === 0 }
55-
onChange={ this.handleAcquisitionChange }
56-
options={ this.props.acquisitionTypeList }
57-
placeholder="Select Acquisition Type"
58-
value={ acquisitionType || null }
59-
/>
60-
</div>
61-
</div>
62-
63119
</div>
64-
<InfoPanel
65-
messages={
66-
[{ propTest: !acquisitionType, colName: 'Acquisition Code' }]
67-
}
68-
/>
69-
</>
70-
)
71-
}
120+
) }
121+
</div>
122+
<InfoPanel
123+
errorMessages={
124+
[{ propTest: this.state.errorMessage, colName: this.state.errorMessage }]
125+
}
126+
messages={
127+
[{ propTest: !acquisitionType, colName: 'Acquisition Code' }]
128+
}
129+
infoMessages={species && species.arcSpeciesCode === 'MA' && numAnimals && numAnimals != 1 && [
130+
{key: 1, value: constants.hamsterWarnings}
131+
]}
132+
/>
133+
</>
134+
)
135+
}
72136
}

snprc_ehr/src/client/NewAnimalPage/components/SaveModal.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ export default class SaveModal extends React.PureComponent {
4141

4242
<Modal.Body>
4343
<SummaryPanel
44-
infoMessages={ [{ key: 1, value: 'Please review data before saving.' },
45-
{ key: 2, value: 'Hover cursor over fields for full text.' }] }
44+
infoMessages={ [...(this.props.numAnimals && this.props.numAnimals != 1 ? [{ key: 1, value: 'Multiple animals are being addeded!' }] : []),
45+
{ key: 2, value: 'Please review data before saving.' },
46+
{ key: 3, value: 'Hover cursor over fields for full text.' }] }
4647
newAnimalData={ this.props.newAnimalData }
48+
numAnimals= { this.props.numAnimals }
4749
/>
4850
</Modal.Body>
4951

snprc_ehr/src/client/NewAnimalPage/components/SummaryPanel.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default class SummaryPanel extends React.Component {
1616
render() {
1717
const { acquisitionType, acqDate, birthDate, gender, dam, sire, animalAccount,
1818
colony, pedigree, iacuc, ownerInstitution, responsibleInstitution, room, cage, diet } = this.props.newAnimalData
19+
const numAnimals = this.props.numAnimals
1920

2021
return (
2122
<>
@@ -51,6 +52,19 @@ export default class SummaryPanel extends React.Component {
5152
/>
5253
</OverlayTrigger>
5354
</div>
55+
{ numAnimals && numAnimals != 1 &&
56+
<div className="summary-panel__col ">
57+
<label className="summary-label summary-label-red">Number of Animals</label>
58+
<OverlayTrigger overlay={ <SummaryPopover message={ numAnimals && numAnimals } title="Number of animals" /> }>
59+
<input
60+
className="summary-text-input summary-text-input-red"
61+
defaultValue={ numAnimals }
62+
onKeyPress={ this.handleKeyPress }
63+
onPasteCapture={ this.handlePaste }
64+
/>
65+
</OverlayTrigger>
66+
</div>
67+
}
5468
</div>
5569
<div className="section-header">Demographics</div>
5670
<div className="summary-panel__row"> {/* Demographics */}
@@ -270,6 +284,7 @@ export default class SummaryPanel extends React.Component {
270284
</div>
271285
<InfoPanel
272286
infoMessages={ this.props.infoMessages }
287+
includeBullets={ true }
273288
/>
274289
</div>
275290
</>

0 commit comments

Comments
 (0)