Skip to content

Commit

Permalink
Merge pull request #18 from HL7-DaVinci/master
Browse files Browse the repository at this point in the history
Fork Auto Sync
  • Loading branch information
smalho01 authored Jun 1, 2022
2 parents c2e9cfe + 76914d8 commit 9bcec59
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 111 deletions.
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"classnames": "^2.2.6",
"fhirclient": "^2.3.1",
"json-server": "^0.16.3",
"jsrsasign": "^8.0.15",
"jsrsasign": "^10.2.0",
"keypair": "^1.0.1",
"lodash": "^4.17.15",
"react": "^16.13.1",
Expand Down
64 changes: 1 addition & 63 deletions src/components/DisplayBox/DisplayBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export default class DisplayBox extends Component{
this.launchSource = this.launchSource.bind(this);
this.renderSource = this.renderSource.bind(this);
this.modifySmartLaunchUrls = this.modifySmartLaunchUrls.bind(this);
this.retrieveLaunchContext = this.retrieveLaunchContext.bind(this);
this.exitSmart = this.exitSmart.bind(this);
this.state={
value: "",
Expand Down Expand Up @@ -203,7 +202,7 @@ export default class DisplayBox extends Component{
let linkCopy = Object.assign({}, link);

if (link.type === 'smart' && (this.props.fhirAccessToken || this.props.ehrLaunch) && !this.state.smartLink) {
this.retrieveLaunchContext(
this.props.retrieveLaunchContext(
linkCopy, this.props.fhirAccessToken,
this.props.patientId, this.props.fhirServerUrl, this.props.fhirVersion
).then((result) => {
Expand All @@ -225,67 +224,6 @@ export default class DisplayBox extends Component{
return undefined;
}

/**
* Retrieves a SMART launch context from an endpoint to append as a "launch" query parameter to a SMART app launch URL (see SMART docs for more about launch context).
* This applies mainly if a SMART app link on a card is to be launched. The link needs a "launch" query param with some opaque value from the SMART server entity.
* This function generates the launch context (for HSPC Sandboxes only) for a SMART application by pinging a specific endpoint on the FHIR base URL and returns
* a Promise to resolve the newly modified link.
* @param {*} link - The SMART app launch URL
* @param {*} accessToken - The access token provided to the CDS Hooks Sandbox by the FHIR server
* @param {*} patientId - The identifier of the patient in context
* @param {*} fhirBaseUrl - The base URL of the FHIR server in context
*/
retrieveLaunchContext(link, accessToken, patientId, fhirBaseUrl, fhirVersion) {
return new Promise((resolve, reject) => {
const headers = accessToken ?
{
"Accept": 'application/json',
"Authorization": `Bearer ${accessToken.access_token}`
}
:
{
"Accept": 'application/json'
};
const launchParameters = {
patient: patientId,
};

if (link.appContext) {
launchParameters.appContext = link.appContext;
}

// May change when the launch context creation endpoint becomes a standard endpoint for all EHR providers
axios({
method: 'post',
url: `${fhirBaseUrl}/_services/smart/Launch`,
headers,
data: {
launchUrl: link.url,
parameters: launchParameters,
},
}).then((result) => {
if (result.data && Object.prototype.hasOwnProperty.call(result.data, 'launch_id')) {
if (link.url.indexOf('?') < 0) {
link.url += '?';
} else {
link.url += '&';
}
link.url += `launch=${result.data.launch_id}`;
link.url += `&iss=${fhirBaseUrl}`;
return resolve(link);
}
console.error('FHIR server endpoint did not return a launch_id to launch the SMART app. See network calls to the Launch endpoint for more details');
link.error = true;
return reject(link);
}).catch((err) => {
console.error('Cannot grab launch context from the FHIR server endpoint to launch the SMART app. See network calls to the Launch endpoint for more details', err);
link.error = true;
return reject(link);
});
});
}


/**
* Helper function to build out the UI for the source of the Card
* @param {*} source - Object as part of the card to build the UI for
Expand Down
121 changes: 116 additions & 5 deletions src/components/RequestBox/RequestBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { defaultValues, shortNameMap } from "../../util/data";
import { getAge } from "../../util/fhir";
import _ from "lodash";
import "./request.css";
import axios from 'axios';

export default class RequestBox extends Component {
constructor(props) {
Expand All @@ -28,7 +29,8 @@ export default class RequestBox extends Component {
insurance: {},
medicationRequest: {},
medicationDispense: {},
gatherCount: 0
gatherCount: 0,
response: {}
};

this.renderRequestResources = this.renderRequestResources.bind(this);
Expand All @@ -39,8 +41,10 @@ export default class RequestBox extends Component {
this.renderResource = this.renderResource.bind(this);
this.renderPrefetchedResources = this.renderPrefetchedResources.bind(this);
this.renderError = this.renderError.bind(this);
this.buildLaunchLink = this.buildLaunchLink.bind(this);
}

// TODO - see how to submit response for alternative therapy
replaceRequestAndSubmit(request) {
let resourceType = request.resourceType.toUpperCase();
console.log("replaceRequestAndSubmit: " + request.resourceType);
Expand Down Expand Up @@ -162,7 +166,8 @@ export default class RequestBox extends Component {
coverage: {},
serviceRequest: {},
medicationRequest: {},
medicationDispense: {}
medicationDispense: {},
response: {}
});
};

Expand Down Expand Up @@ -514,6 +519,7 @@ export default class RequestBox extends Component {
State: {this.state.patientState ? this.state.patientState : "N/A"}
</div>
{this.renderOtherInfo()}
{this.renderQRInfo()}
</div>
);
}
Expand All @@ -538,6 +544,24 @@ export default class RequestBox extends Component {
);
}

renderQRInfo() {
const qrResponse = this.state.response;
return (
<div className="questionnaire-response">
<div className="lower-border">
<span style={{ fontWeight: "bold" }}>In Progress Form</span>
</div>
<div className="info lower-border">Form: { qrResponse.questionnaire ? qrResponse.questionnaire : "N/A"}</div>
<div className="info lower-border">
Author: {qrResponse.author ? qrResponse.author.reference : "N/A"}
</div>
<div className="info lower-border">
Date: {qrResponse.authored ? qrResponse.authored : "N/A"}
</div>
</div>
);
}

renderPrefetchedResources() {
const deviceRequestResources = [
"patient",
Expand Down Expand Up @@ -635,12 +659,94 @@ export default class RequestBox extends Component {
);
}

/**
* Relaunch DTR using the available context
*/
relaunch = (e) => {
this.buildLaunchLink()
.then(link => {
//e.preventDefault();
window.open(link.url, "_blank");
});
}

buildLaunchLink() {
// build appContext and URL encode it
let appContext = "";
let order = undefined, coverage = undefined, response = undefined;

if (!this.isOrderNotSelected()) {
if (Object.keys(this.state.deviceRequest).length > 0) {
order = `${this.state.deviceRequest.resourceType}/${this.state.deviceRequest.id}`;

if (this.state.deviceRequest.insurance && this.state.deviceRequest.insurance.length > 0) {
coverage = `${this.state.deviceRequest.insurance[0].reference}`;
}
} else if (Object.keys(this.state.serviceRequest).length > 0) {
order = `${this.state.serviceRequest.resourceType}/${this.state.serviceRequest.id}`;

if (this.state.serviceRequest.insurance && this.state.serviceRequest.insurance.length > 0) {
coverage = `${this.state.serviceRequest.insurance[0].reference}`;
}
} else if (Object.keys(this.state.medicationRequest).length > 0) {
order = `${this.state.medicationRequest.resourceType}/${this.state.medicationRequest.id}`;

if (this.state.medicationRequest.insurance && this.state.medicationRequest.insurance.length > 0) {
coverage = `${this.state.medicationRequest.insurance[0].reference}`;
}
} else if (Object.keys(this.state.medicationDispense).length > 0) {
order = `${this.state.medicationDispense.resourceType}/${this.state.medicationDispense.id}`;
}
}

if(order) {
appContext += `order=${order}`

if(coverage) {
appContext += `&coverage=${coverage}`
}
}

if(Object.keys(this.state.response).length > 0) {
response = `QuestionnaireResponse/${this.state.response.id}`;
}

if(order && response) {
appContext += `&response=${response}`
} else if (!order && response) {
appContext += `response=${response}`
}

const link = {
appContext: encodeURIComponent(appContext),
type: "smart",
url: this.props.launchUrl
}

let linkCopy = Object.assign({}, link);

return this.props.retrieveLaunchContext(
linkCopy, this.props.fhirAccessToken,
this.state.patient.id, this.props.fhirServerUrl, this.props.fhirVersion
).then((result) => {
linkCopy = result;
return linkCopy;
});
}

isOrderNotSelected() {
return Object.keys(this.state.deviceRequest).length === 0 && Object.keys(this.state.serviceRequest).length === 0
&& Object.keys(this.state.medicationRequest).length === 0 && Object.keys(this.state.medicationDispense).length === 0;
}

render() {
const params = {};
params['serverUrl'] = this.props.ehrUrl;
if (this.props.access_token) {
params['tokenResponse'] = {access_token: this.props.access_token.access_token};
}
const disableSendToCRD = this.isOrderNotSelected();
const disableLaunchDTR = this.isOrderNotSelected() && Object.keys(this.state.response).length === 0;
return (
<div>
<div className="request">
Expand Down Expand Up @@ -672,6 +778,7 @@ export default class RequestBox extends Component {
clearCallback={this.clearState}
ehrUrl={this.props.ehrUrl}
options={this.state.codeValues}
responseExpirationDays={this.props.responseExpirationDays}
/>
);
})}
Expand All @@ -692,11 +799,15 @@ export default class RequestBox extends Component {
<div>
{this.renderPatientInfo()}
{this.renderPrefetchedResources()}
</div>
</div>

</div>
</div>
<button className={"submit-btn btn btn-class "} onClick={this.submit}>
Submit
<button className={"submit-btn btn btn-class "} onClick={this.relaunch} disabled={disableLaunchDTR}>
Relaunch DTR
</button>
<button className={"submit-btn btn btn-class "} onClick={this.submit} disabled={disableSendToCRD}>
Submit to CRD
</button>
</div>
);
Expand Down
14 changes: 13 additions & 1 deletion src/components/RequestBox/request.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

.request {
border: 1px solid black;
height:500px;
height:530px;
padding: 10px;
border-radius: 5px;
background-color: rgb(248, 248, 248)
Expand Down Expand Up @@ -86,6 +86,13 @@
margin-top:8px;
}

.submit-btn:disabled,
.submit-btn[disabled] {
border: 1px solid #999999;
background-color: #333232;
color: #858282;
}

.patient-box {
overflow-y:scroll;
height:800px;
Expand All @@ -99,4 +106,9 @@
background-color: #facbcbcc;
margin: 10px 30% 10px 30%;
display:block;
}

.questionnaire-response {
width: 100%;
padding:10px 10px 10px 0px;
}
Loading

0 comments on commit 9bcec59

Please sign in to comment.