Skip to content

Commit 394ffbb

Browse files
authored
Merge pull request #170 from mcode/dev
Dev
2 parents b9fd87e + 5730f59 commit 394ffbb

File tree

27 files changed

+572
-351
lines changed

27 files changed

+572
-351
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,7 @@ VITE_USER = alice
2626
VITE_HOOK_TO_SEND = patient-view
2727
VITE_URL_FILTER = http://localhost:3000/*
2828
VITE_USE_INTERMEDIARY = false
29+
VITE_USE_PHARMACY_IN_PREFETCH = true
2930
VITE_INTERMEDIARY = http://localhost:3003
3031
VITE_DISABLE_MEDICATION_STATUS = false
32+
VITE_PHARMACY_ID = pharm0111

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,38 @@ Following are a list of modifiable paths:
134134
| VITE_URL | `http://localhost:3000` | The base url of this app. Should be modified if the port or domain change. |
135135
| VITE_USER | `alice` | The default user to login as when opening the app. |
136136
| VITE_USE_INTERMEDIARY | false | When true, the app will send all CDS Hooks and REMS ETASU check calls to the intermediary defined in VITE_INTERMEDIARY. |
137-
| VITE_INTERMEDIARY | `http://localhost:3030` | The base url of the intermediary. |
137+
| VITE_INTERMEDIARY | `http:/localhost:3030` | The base url of the intermediary. |
138+
| VITE_USE_PHARMACY_IN_PREFETCH | true | When true, the app will send pharmacy information to the rems admin in the CDS Hooks prefetch |
139+
| VITE_PHARMACY_ID | `pharm0111` | The pharmacy ID to use in the CDS Hooks Prefetch |
140+
141+
# Data Rights
142+
This repository has been forked from the [HL7-DaVinci/crd-request-generator](https://github.com/HL7-DaVinci/crd-request-generator) repository. As such, the following data rights apply to all changes made on this fork of the repository, starting with release 0.1 and onward.
143+
144+
<div style="text-align:center">
145+
<b>NOTICE</b>
146+
</div>
147+
148+
This (software/technical data) was produced for the U. S. Government under Contract Number 75FCMC18D0047/75FCMC23D0004, and is subject to Federal Acquisition Regulation Clause 52.227-14, Rights in Data-General.
149+
150+
151+
No other use other than that granted to the U. S. Government, or to those acting on behalf of the U. S. Government under that Clause is authorized without the express written permission of The MITRE Corporation.
152+
153+
154+
For further information, please contact The MITRE Corporation, Contracts Management Office, 7515 Colshire Drive, McLean, VA 22102-7539, (703) 983-6000.
155+
156+
<div style="text-align:center">
157+
<b>&copy;2025 The MITRE Corporation.</b>
158+
</div>
159+
160+
<br />
161+
162+
Licensed under the Apache License, Version 2.0 (the "License"); use of this repository is permitted in compliance with the License.
163+
You may obtain a copy of the License at
164+
165+
http://www.apache.org/licenses/LICENSE-2.0
166+
167+
Unless required by applicable law or agreed to in writing, software
168+
distributed under the License is distributed on an "AS IS" BASIS,
169+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
170+
See the License for the specific language governing permissions and
171+
limitations under the License.

src/PrefetchTemplate.js

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
// Prefetch Template Source:
22
// https://build.fhir.org/ig/HL7/davinci-crd/hooks.html#prefetch
33
export class PrefetchTemplate {
4-
static generatePrefetchMap() {
4+
static generatePrefetchMap(settings = null) {
5+
// If no settings provided, use defaults from data.js
6+
const includePharmacy = settings?.includePharmacyInPreFetch ??
7+
headerDefinitions.includePharmacyInPreFetch.default;
8+
const pharmacyId = settings?.pharmacyId ??
9+
headerDefinitions.pharmacyId.default;
10+
511
const prefetchMap = new Map();
612

713
const PRACTITIONER_PREFETCH = new PrefetchTemplate('{{context.userId}}');
8-
914
const REQUEST_PREFETCH = new PrefetchTemplate(
1015
'MedicationRequest/{{context.medications.MedicationRequest.id}}'
1116
);
1217
const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}');
13-
1418
const ALL_REQUESTS_PREFETCH = new PrefetchTemplate(
1519
'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication'
1620
);
1721

18-
// prefetchMap.set("Coverage", COVERAGE_PREFETCH_QUERY);
22+
// Core prefetch items (always included)
1923
prefetchMap.set('request', REQUEST_PREFETCH);
2024
prefetchMap.set('practitioner', PRACTITIONER_PREFETCH);
2125
prefetchMap.set('patient', PATIENT_PREFETCH);
2226
prefetchMap.set('medicationRequests', ALL_REQUESTS_PREFETCH);
23-
// prefetchMap.set("ServiceRequest", SERVICE_REQUEST_BUNDLE);
24-
// prefetchMap.set("Encounter", ENCOUNTER_BUNDLE);
27+
28+
// Optional pharmacy prefetch based on settings
29+
if (includePharmacy && pharmacyId) {
30+
const PHARMACY_PREFETCH = new PrefetchTemplate(`HealthcareService/${pharmacyId}`);
31+
prefetchMap.set('pharmacy', PHARMACY_PREFETCH);
32+
}
2533

2634
return prefetchMap;
2735
}
@@ -47,27 +55,49 @@ export class PrefetchTemplate {
4755
return paramElementMap;
4856
}
4957

50-
static generateQueries(requestBundle, patientReference, userReference, ...prefetchKeys) {
58+
static generateQueries(
59+
requestBundle,
60+
patientReference,
61+
userReference,
62+
settings = null,
63+
...prefetchKeys
64+
) {
65+
const prefetchMap = PrefetchTemplate.generatePrefetchMap(settings);
66+
const paramElementMap = PrefetchTemplate.generateParamElementMap();
67+
5168
var resolvedQueries = new Map();
5269
for (var i = 0; i < prefetchKeys.length; i++) {
5370
var prefetchKey = prefetchKeys[i];
71+
if (!prefetchKey || !prefetchMap.has(prefetchKey)) continue;
5472
var query = prefetchMap.get(prefetchKey).getQuery();
5573
// Regex source: https://regexland.com/all-between-specified-characters/
5674
var parametersToFill = query.match(/(?<={{).*?(?=}})/gs);
5775
var resolvedQuery = query.slice();
58-
for (var j = 0; j < parametersToFill.length; j++) {
59-
var unresolvedParameter = parametersToFill[j];
60-
var resolvedParameter;
61-
if (requestBundle) {
62-
resolvedParameter = PrefetchTemplate.resolveParameter(unresolvedParameter, requestBundle);
63-
} else {
64-
if (unresolvedParameter === 'context.patientId') {
65-
resolvedParameter = patientReference;
66-
} else if (unresolvedParameter === 'context.userId') {
67-
resolvedParameter = userReference;
76+
77+
if (parametersToFill) {
78+
for (var j = 0; j < parametersToFill.length; j++) {
79+
var unresolvedParameter = parametersToFill[j];
80+
var resolvedParameter;
81+
if (requestBundle) {
82+
resolvedParameter = PrefetchTemplate.resolveParameter(
83+
unresolvedParameter,
84+
requestBundle,
85+
paramElementMap
86+
);
87+
} else {
88+
if (unresolvedParameter === 'context.patientId') {
89+
resolvedParameter = patientReference;
90+
} else if (unresolvedParameter === 'context.userId') {
91+
resolvedParameter = userReference;
92+
}
93+
}
94+
if (resolvedParameter) {
95+
resolvedQuery = resolvedQuery.replace(
96+
'{{' + unresolvedParameter + '}}',
97+
resolvedParameter
98+
);
6899
}
69100
}
70-
resolvedQuery = resolvedQuery.replace('{{' + unresolvedParameter + '}}', resolvedParameter);
71101
}
72102
resolvedQueries.set(prefetchKey, resolvedQuery);
73103
}
@@ -89,8 +119,9 @@ export class PrefetchTemplate {
89119
}
90120
}
91121

92-
static resolveParameter(unresolvedParameter, requestBundle) {
122+
static resolveParameter(unresolvedParameter, requestBundle, paramElementMap) {
93123
const paramField = paramElementMap.get(unresolvedParameter);
124+
if (!paramField) return null;
94125
const resolvedParameter = PrefetchTemplate.getProp(requestBundle, paramField);
95126
return resolvedParameter;
96127
}
@@ -104,7 +135,4 @@ export class PrefetchTemplate {
104135
getQuery() {
105136
return this.query;
106137
}
107-
}
108-
109-
const prefetchMap = PrefetchTemplate.generatePrefetchMap();
110-
const paramElementMap = PrefetchTemplate.generateParamElementMap();
138+
}

src/components/App.jsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,24 @@ const App = () => {
2828
<Router>
2929
<Routes>
3030
<Route path="/launch" element={<Launch redirect={redirect} />} />
31-
<Route path="/index" element={<ThemeProvider theme={theme}><Index/></ThemeProvider>} />
31+
<Route
32+
path="/index"
33+
element={
34+
<ThemeProvider theme={theme}>
35+
<Index />
36+
</ThemeProvider>
37+
}
38+
/>
3239
<Route path="/register" element={<RegisterPage />} />
3340
{/* forcibly enter backoffice workflow */}
34-
<Route path="/index/backoffice" element={<ThemeProvider theme={theme}><Index backoffice={true}/></ThemeProvider> } />
41+
<Route
42+
path="/index/backoffice"
43+
element={
44+
<ThemeProvider theme={theme}>
45+
<Index backoffice={true} />
46+
</ThemeProvider>
47+
}
48+
/>
3549
<Route
3650
path="/patient-portal"
3751
element={

src/components/DisplayBox/DisplayBox.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,13 @@ const DisplayBox = props => {
355355
{summarySection}
356356
</Typography>
357357

358+
<div>
359+
<Typography variant="div">{detailSection}</Typography>
360+
</div>
361+
358362
{/* Forms */}
359363
{linksSection.length !== 0 ? (
360364
<div>
361-
<Typography variant="div">{detailSection}</Typography>
362365
<Typography color="text.secondary">Required Forms</Typography>
363366
<List className={'links-section'}>{linksSection}</List>
364367
</div>

src/components/RequestBox/PatientSearchBar/PatientSearchBar.jsx

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,33 @@ const PatientSearchBar = props => {
4545
<Box className="search-box-container">
4646
<Grid container>
4747
<Grid item xs={9}>
48-
<span className="search-header">
49-
<p>Filter patient list</p>
50-
<Autocomplete
51-
className="search-box"
52-
disablePortal
53-
id="search-box"
54-
onInputChange={(event, newInputValue) => {
55-
setInput(newInputValue.toLowerCase());
56-
}}
57-
options={listOfPatients[0].map(item => item.name)}
58-
renderInput={params => <TextField {...params} label="Search" />}
59-
/>
60-
<p>
61-
Showing {getFilteredLength(input, listOfPatients)} of {props.searchablePatients.length}{' '}
62-
records
63-
</p>
64-
</span>
48+
<span className="search-header">
49+
<p>Filter patient list</p>
50+
<Autocomplete
51+
className="search-box"
52+
disablePortal
53+
id="search-box"
54+
onInputChange={(event, newInputValue) => {
55+
setInput(newInputValue.toLowerCase());
56+
}}
57+
options={listOfPatients[0].map(item => item.name)}
58+
renderInput={params => <TextField {...params} label="Search" />}
59+
/>
60+
<p>
61+
Showing {getFilteredLength(input, listOfPatients)} of{' '}
62+
{props.searchablePatients.length} records
63+
</p>
64+
</span>
6565
</Grid>
6666
<Grid item xs={3}>
67-
<Button variant="contained" startIcon={<PeopleIcon />} onClick={() => { showAllPatients(); }} style={{padding:'10px','paddingLeft':'20px', 'paddingRight':'20px'}}>
67+
<Button
68+
variant="contained"
69+
startIcon={<PeopleIcon />}
70+
onClick={() => {
71+
showAllPatients();
72+
}}
73+
style={{ padding: '10px', paddingLeft: '20px', paddingRight: '20px' }}
74+
>
6875
Select all Patients
6976
</Button>
7077
</Grid>

src/components/RequestBox/RequestBox.jsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,7 @@ const RequestBox = props => {
182182

183183
let userId = prefetchedResources?.practitioner?.id;
184184
if (!userId) {
185-
console.log(
186-
'Practitioner not populated from prefetch, using user: ' + user
187-
);
185+
console.log('Practitioner not populated from prefetch, using user: ' + user);
188186
userId = user;
189187
}
190188

src/components/RequestDashboard/Home.jsx

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -64,36 +64,37 @@ const Home = props => {
6464
}
6565
return (
6666
<div>
67-
<Grid className={gridClass} item container justifyContent={'center'} alignItems={'center'}>
68-
{section ? '' : <Grid item xs={3}></Grid>} {/* spacer */}
69-
{renderMainButton(patientButton, <PersonIcon className={classes.mainIcon} />)}
70-
{renderMainButton(taskButton, <AssignmentIcon className={classes.mainIcon} />)}
71-
{renderMainButton(settingsButton, <SettingsIcon className={classes.mainIcon} />)}
72-
{section ? (
73-
<Grid className={classes.spacer} item xs={0}>
74-
<span className={classes.titleIcon}>
75-
<MedicalServicesIcon sx={{ fontSize: 48, verticalAlign: 'middle' }} />&nbsp;&nbsp;<strong>EHR</strong> Request Generator
76-
</span>
77-
</Grid>
78-
) : (
79-
<Grid item xs={3}></Grid>
80-
)}
81-
{/* spacer */}
82-
{/** */}
83-
{section ? (
84-
<Grid className={classes.spacer} item xs={4}>
85-
<span className={classes.loginIcon}>
86-
<AccountBoxIcon sx={{ fontSize: 48, verticalAlign: 'middle' }} /> {token.name}
87-
<Button variant="outlined" className={classes.whiteButton} onClick={logout}>
88-
Logout
89-
</Button>
90-
</span>
91-
</Grid>
92-
) : (
93-
<Grid item xs={3}></Grid>
94-
)}
95-
{/**/}
96-
</Grid>
67+
<Grid className={gridClass} item container justifyContent={'center'} alignItems={'center'}>
68+
{section ? '' : <Grid item xs={3}></Grid>} {/* spacer */}
69+
{renderMainButton(patientButton, <PersonIcon className={classes.mainIcon} />)}
70+
{renderMainButton(taskButton, <AssignmentIcon className={classes.mainIcon} />)}
71+
{renderMainButton(settingsButton, <SettingsIcon className={classes.mainIcon} />)}
72+
{section ? (
73+
<Grid className={classes.spacer} item xs={0}>
74+
<span className={classes.titleIcon}>
75+
<MedicalServicesIcon sx={{ fontSize: 48, verticalAlign: 'middle' }} />
76+
&nbsp;&nbsp;<strong>EHR</strong> Request Generator
77+
</span>
78+
</Grid>
79+
) : (
80+
<Grid item xs={3}></Grid>
81+
)}
82+
{/* spacer */}
83+
{/** */}
84+
{section ? (
85+
<Grid className={classes.spacer} item xs={4}>
86+
<span className={classes.loginIcon}>
87+
<AccountBoxIcon sx={{ fontSize: 48, verticalAlign: 'middle' }} /> {token.name}
88+
<Button variant="outlined" className={classes.whiteButton} onClick={logout}>
89+
Logout
90+
</Button>
91+
</span>
92+
</Grid>
93+
) : (
94+
<Grid item xs={3}></Grid>
95+
)}
96+
{/**/}
97+
</Grid>
9798
</div>
9899
);
99100
};

src/components/RequestDashboard/PatientSection.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ const PatientSection = props => {
88
return (
99
<div>
1010
{state.startup ? (
11-
<RequestBuilder globalState={state} dispatch={dispatch} client={props.client} userId={props.userId} />
11+
<RequestBuilder
12+
globalState={state}
13+
dispatch={dispatch}
14+
client={props.client}
15+
userId={props.userId}
16+
/>
1217
) : (
1318
<>Loading...</>
1419
)}

0 commit comments

Comments
 (0)