Skip to content

Commit 3dfc074

Browse files
authored
New dev - added birth record report page to more reports (#262)
* New dev - added birth record report page to more reports * Fixed typo
1 parent bb43d53 commit 3dfc074

28 files changed

Lines changed: 1381 additions & 2 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ snprc_scheduler/resources/web/snprc_scheduler/app/app.js
1515
snprc_ehr/resources/views/HelloApp*.*
1616
snprc_ehr/resources/views/helloWorld*.*
1717
snprc_ehr/resources/views/NewAnimalPage*.*
18+
snprc_ehr/resources/views/BirthRecordReport*.*
1819
snprc_ehr/cmd-here.exe
1920
snprc_ehr/resources/referenceStudy/*.xlsx
2021
snprc_ehr/resources/web/snprc_ehr/snprcReports.js.gz
2122
snprc_ehr/resources/web/snprc_ehr/gen/*
2223
snprc_mobile
2324
/snprc_ehr.2020*.zip
2425
/snprc_ehr.2020*.7z
26+
/snprc_ehr/.editorconfig
27+
/snprc_ehr/cmd-here.exe
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* eslint-disable no-alert */
2+
3+
import React from 'react'
4+
import { LoadingSpinner } from '@labkey/components'
5+
import './styles/birthRecordReport.scss'
6+
import { Button } from 'react-bootstrap'
7+
import BirthRecordState from './constants/BirthRecordState'
8+
import AnimalSelectionPanel from './components/AnimalSelectionPanel'
9+
import fetchNewAnimalData from './api/fetchNewAnimalData'
10+
import SummaryGridPanel from './components/SummaryGridPanel'
11+
import { getReportPath } from './services/printToPDF'
12+
import InfoPanel from './components/InfoPanel'
13+
14+
export default class BirthRecordReport extends React.Component {
15+
state = new BirthRecordState();
16+
17+
componentDidMount() {
18+
Promise.resolve(this.loadLists()).catch = error => {
19+
console.log(`Error in componentDidMount: ${error}`)
20+
}
21+
}
22+
23+
loadLists() {
24+
let animalList = []
25+
26+
return new Promise(() => {
27+
(
28+
fetchNewAnimalData().then(list => {
29+
animalList = list
30+
})
31+
).then(() => {
32+
this.setState(prevState => (
33+
{
34+
...prevState,
35+
isLoading: false,
36+
animalList
37+
}
38+
))
39+
}).catch(error => {
40+
console.log(`Error in loadLists: ${error}`)
41+
})
42+
})
43+
}
44+
45+
// quit
46+
onQuitClick = () => {
47+
window.history.back()
48+
}
49+
50+
onClickPrint = () => {
51+
if (!this.state.selectedAnimal) {
52+
this.setState(prevState => (
53+
{
54+
...prevState,
55+
errorMessage: 'Select an animal before clicking print.'
56+
}))
57+
} else {
58+
const reportPath = getReportPath('BirthRecord')
59+
const fullPath = `${reportPath}&rc:Parameters=Collapsed&TargetID=${this.state.selectedAnimal.Id}`
60+
const left = window.screenX + 20
61+
62+
window.open(fullPath, '_blank', `location=yes,height=850,width=768,status=yes, left=${left}`)
63+
64+
// add animal to the summary list if it is not there
65+
if (!this.state.summaryData.find(o => o.Id === this.state.selectedAnimal.Id)) {
66+
this.setState(prevState => (
67+
{
68+
...prevState,
69+
summaryData: [
70+
...prevState.summaryData,
71+
{ ...prevState.selectedAnimal }
72+
]
73+
}))
74+
}
75+
}
76+
}
77+
78+
handleChange = option => {
79+
this.setState(prevState => (
80+
{
81+
...prevState,
82+
errorMessage: undefined,
83+
selectedAnimal: option
84+
}
85+
))
86+
}
87+
88+
render() {
89+
const { isLoading } = this.state
90+
91+
if (isLoading) {
92+
return (
93+
<LoadingSpinner msg="Loading app..." />
94+
)
95+
}
96+
97+
return (
98+
<div>
99+
<div className="split-panel">
100+
<div className="parent-panel">
101+
<div className="panel-heading">
102+
<p>Select Animal</p>
103+
</div>
104+
<div className="wizard-panel">
105+
<AnimalSelectionPanel
106+
animalList={ this.state.animalList }
107+
handleChange={ this.handleChange }
108+
selectedAnimal={ this.state.selectedAnimal }
109+
/>
110+
<InfoPanel
111+
errorMessages={ this.state.errorMessage
112+
&& [{ propTest: true, colName: this.state.errorMessage }] }
113+
infoMessages={ [{ key: 1, value: 'Select an animal and press the print button to generate the Birth Record Report.' }] }
114+
/>
115+
</div>
116+
</div>
117+
<div className="right-panel">
118+
<div className="panel-heading">
119+
<p>Reports Printed</p>
120+
</div>
121+
<div className="wizard-right">
122+
<SummaryGridPanel
123+
summaryData={ this.state.summaryData }
124+
/>
125+
</div>
126+
</div>
127+
</div>
128+
129+
<div className="wizard-footer">
130+
<Button bsStyle="primary" onClick={ this.onClickPrint }>Print Birth Record <i aria-hidden="true" className="fa fa-print" /></Button>
131+
<Button onClick={ this.onQuitClick }>Quit</Button>
132+
</div>
133+
</div>
134+
)
135+
}
136+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* eslint-disable no-unused-vars, camelcase */
2+
3+
import { NewAnimalData } from '../../tests/fixtures/apiTestData'
4+
5+
export const request = ({ schemaName, queryName, viewName = '', sort = '', columns = [], filterArray = [] }) => {
6+
// console.log(`loading list: ${queryName}`);
7+
return new Promise(resolve => {
8+
switch (queryName) {
9+
case 'NewAnimalData':
10+
resolve(NewAnimalData)
11+
break
12+
default:
13+
break
14+
}
15+
})
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Query } from '@labkey/api'
2+
3+
export const request = ({ schemaName, queryName, viewName = '', sort = '', columns = [], filterArray = [] }) => {
4+
return new Promise((resolve, reject) => {
5+
const success = response => {
6+
resolve(response)
7+
}
8+
const failure = error => {
9+
reject(error)
10+
}
11+
Query.selectRows({
12+
requiredVersion: 19.2,
13+
schemaName,
14+
queryName,
15+
viewName,
16+
sort,
17+
columns,
18+
success,
19+
failure,
20+
filterArray
21+
})
22+
})
23+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Filter } from '@labkey/api'
2+
import moment from 'moment'
3+
import { request } from './api'
4+
5+
const parse = rows => {
6+
return rows.map(({ data }) => {
7+
return {
8+
Id: data.Id.value,
9+
value: data.Id.value,
10+
label: `Id: ${data.Id.value} - ${data.gender.displayValue} - Birth Date: ${moment(data.BirthDate.value).format('MM/DD/YYYY h:mm A')} `,
11+
BirthDate: data.BirthDate.value,
12+
BirthCode: data.BirthCode.value,
13+
AcquisitionType: data.AcquisitionType.value,
14+
AcqDate: data.AcqDate.value,
15+
gender: data.gender.value,
16+
sire: data.sire.value,
17+
dam: data.dam.value,
18+
species: data.species.value,
19+
Colony: data.Colony.value,
20+
AnimalAccount: data.AnimalAccount.value,
21+
OwnerInstitution: data.OwnerInstitution.value,
22+
ResponsibleInstitution: data.ResponsibleInstitution.value,
23+
room: data.room.value,
24+
Cage: data.Cage.value,
25+
Diet: data.Diet.value,
26+
pedigree: data.pedigree.value,
27+
IACUC: data.IACUC.value
28+
}
29+
})
30+
}
31+
32+
const fetchNewAnimalData = () => {
33+
return new Promise((resolve, reject) => {
34+
request({
35+
schemaName: 'snprc_ehr',
36+
queryName: 'NewAnimalData',
37+
columns:
38+
[
39+
'Id',
40+
'BirthDate',
41+
'BirthCode',
42+
'AcquisitionType',
43+
'AcqDate',
44+
'gender',
45+
'sire',
46+
'Dam',
47+
'Species',
48+
'Colony',
49+
'AnimalAccount',
50+
'OwnerInstitution',
51+
'ResponsibleInstitution',
52+
'room',
53+
'Cage',
54+
'Diet',
55+
'pedigree',
56+
'IACUC'
57+
],
58+
59+
sort: 'Id',
60+
filterArray: [
61+
Filter.create('AcquisitionType/category', 'Birth', Filter.Types.EQUAL)
62+
]
63+
}).then(({ rows }) => {
64+
resolve(parse(rows))
65+
}).catch(error => {
66+
reject(error)
67+
console.log('error', error)
68+
})
69+
})
70+
}
71+
export default fetchNewAnimalData
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
4+
import BirthRecordReport from './BirthRecordReport'
5+
6+
// Need to wait for container element to be available in labkey wrapper before render
7+
window.addEventListener('DOMContentLoaded', () => {
8+
ReactDOM.render(<BirthRecordReport />, document.getElementById('app'))
9+
})
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react'
2+
import Select from 'react-select'
3+
4+
export default class AnimalSelectionPanel extends React.Component {
5+
handleChange = option => {
6+
this.props.handleChange(option)
7+
}
8+
9+
render() {
10+
return (
11+
<>
12+
<div className="wizard-panel__rows">
13+
<div className="wizard-panel__row">
14+
<div className="wizard-panel__col">
15+
<label className="field-label-align-close">Animal</label>
16+
<Select
17+
autoFocus
18+
className="shared-dropdown"
19+
classNamePrefix="shared-select"
20+
id="animal-select"
21+
isClearable
22+
isLoading={ !this.props.animalList }
23+
onChange={ this.handleChange }
24+
options={ this.props.animalList }
25+
placeholder="Select Animal"
26+
value={ this.props.selectedAnimal || null }
27+
/>
28+
</div>
29+
</div>
30+
</div>
31+
</>
32+
)
33+
}
34+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react'
2+
3+
export default class InfoPanel extends React.PureComponent {
4+
render() {
5+
const messages = this.props.messages && this.props.messages
6+
const errorMessages = this.props.errorMessages && this.props.errorMessages
7+
const infoMessages = this.props.infoMessages && this.props.infoMessages
8+
9+
return (
10+
<>
11+
<div className="info-panel">
12+
<span>
13+
{messages
14+
&& messages.some(message => { return message.propTest })
15+
&& 'Enter values for the following fields to continue: '}
16+
{
17+
messages
18+
&& messages.map(message => {
19+
return message.propTest && <span key={ message.colName }> <span className="info-span"> &nbsp;{message.colName}&nbsp; </span> &nbsp; </span>
20+
})
21+
22+
}
23+
24+
{errorMessages
25+
&& (
26+
<div className="info-err-div">
27+
{errorMessages.map(message => {
28+
return message.propTest && <span key={ message.colName }> <span className="info-err-span"> &nbsp;{message.colName}&nbsp; </span> &nbsp; </span>
29+
})}
30+
</div>
31+
)}
32+
{infoMessages
33+
&& (
34+
<span className="info-div">
35+
{infoMessages.map(message => {
36+
return <div key={ message.key } className="info-text-span">{message.value}</div>
37+
})}
38+
</span>
39+
)}
40+
41+
</span>
42+
</div>
43+
</>
44+
)
45+
}
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react'
2+
import moment from 'moment'
3+
4+
export default class SummaryGridItem extends React.PureComponent {
5+
onClickHandler = () => {
6+
const { id } = this.props.row
7+
this.props.print(id)
8+
}
9+
10+
render() {
11+
const { Id, gender, BirthDate } = this.props.row
12+
return (
13+
<tr>
14+
<td>
15+
{Id}
16+
</td>
17+
<td>
18+
{gender}
19+
</td>
20+
<td>
21+
{moment(BirthDate).format('MM/DD/YYYY h:mm A')}
22+
</td>
23+
</tr>
24+
)
25+
}
26+
}

0 commit comments

Comments
 (0)