diff --git a/.env b/.env
index d4b9e1e..f6a3868 100644
--- a/.env
+++ b/.env
@@ -30,3 +30,5 @@ VITE_USE_PHARMACY_IN_PREFETCH = true
VITE_INTERMEDIARY = http://localhost:3003
VITE_DISABLE_MEDICATION_STATUS = false
VITE_PHARMACY_ID = pharm0111
+VITE_PACIO_EHR_URL = https://gw.interop.community/paciosandbox2/open/Bundle
+VITE_PACIO_NEW_PRESCRIBER_ID=pra1234
diff --git a/README.md b/README.md
index 3608494..34a248f 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,13 @@ Following are a list of modifiable paths:
| 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. |
| VITE_INTERMEDIARY | `http:/localhost:3030` | The base url of the intermediary. |
| VITE_USE_PHARMACY_IN_PREFETCH | true | When true, the app will send pharmacy information to the rems admin in the CDS Hooks prefetch |
-| VITE_PHARMACY_ID | `pharm0111` | The pharmacy ID to use in the CDS Hooks Prefetch |
+| VITE_PHARMACY_ID | `pharm0111` | The pharmacy ID to use in the CDS Hooks Prefetch |
+| HTTPS | `false` | Enable HTTPS for the server. Set to `true` to use HTTPS with valid certificate and key paths. |
+| HTTPS_CERT_PATH | `server.cert` | Path to a certificate for encryption, allowing HTTPS. Unnecessary if using HTTP. |
+| HTTPS_KEY_PATH | `server.key` | Path to a key for encryption, allowing HTTPS. Unnecessary if using HTTP. |
+| VITE_HOOK_TO_SEND | `patient-view` | Specifies which CDS Hook type to send by default (e.g., patient-view, order-sign, order-select, encounter-start). |
+| VITE_URL_FILTER | `http://localhost:3000/*` | URL filtering pattern for request validation. |
+| VITE_DISABLE_MEDICATION_STATUS | `false` | When set to true, disables the medication status display in the UI. |
# Data Rights
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.
diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js
index 573a880..7ee00ca 100644
--- a/src/PrefetchTemplate.js
+++ b/src/PrefetchTemplate.js
@@ -3,10 +3,9 @@
export class PrefetchTemplate {
static generatePrefetchMap(settings = null) {
// If no settings provided, use defaults from data.js
- const includePharmacy = settings?.includePharmacyInPreFetch ??
- headerDefinitions.includePharmacyInPreFetch.default;
- const pharmacyId = settings?.pharmacyId ??
- headerDefinitions.pharmacyId.default;
+ const includePharmacy =
+ settings?.includePharmacyInPreFetch ?? headerDefinitions.includePharmacyInPreFetch.default;
+ const pharmacyId = settings?.pharmacyId ?? headerDefinitions.pharmacyId.default;
const prefetchMap = new Map();
@@ -64,7 +63,7 @@ export class PrefetchTemplate {
) {
const prefetchMap = PrefetchTemplate.generatePrefetchMap(settings);
const paramElementMap = PrefetchTemplate.generateParamElementMap();
-
+
var resolvedQueries = new Map();
for (var i = 0; i < prefetchKeys.length; i++) {
var prefetchKey = prefetchKeys[i];
@@ -73,7 +72,7 @@ export class PrefetchTemplate {
// Regex source: https://regexland.com/all-between-specified-characters/
var parametersToFill = query.match(/(?<={{).*?(?=}})/gs);
var resolvedQuery = query.slice();
-
+
if (parametersToFill) {
for (var j = 0; j < parametersToFill.length; j++) {
var unresolvedParameter = parametersToFill[j];
@@ -135,4 +134,4 @@ export class PrefetchTemplate {
getQuery() {
return this.query;
}
-}
\ No newline at end of file
+}
diff --git a/src/components/RequestBox/RequestBox.jsx b/src/components/RequestBox/RequestBox.jsx
index c402427..7fbd0b5 100644
--- a/src/components/RequestBox/RequestBox.jsx
+++ b/src/components/RequestBox/RequestBox.jsx
@@ -220,8 +220,13 @@ const RequestBox = props => {
* Send NewRx for new Medication to the Pharmacy Information System (PIMS)
*/
const sendRx = async () => {
- console.log('Sending NewRx to: ' + pimsUrl);
- console.log('Getting case number ');
+ // Use intermediary or direct based on toggle
+ const ncpdpEndpoint = globalState.usePharmacyIntermediary
+ ? globalState.pharmacyIntermediaryUrl
+ : pimsUrl;
+
+ console.log('Sending NewRx to: ' + ncpdpEndpoint);
+ console.log('Getting case number');
const medication = createMedicationFromMedicationRequest(request);
const body = makeBody(medication);
const standardEtasuUrl = getMedicationSpecificEtasuUrl(
@@ -260,7 +265,7 @@ const RequestBox = props => {
const serializer = new XMLSerializer();
// Sending NewRx to the Pharmacy
- fetch(pimsUrl, {
+ fetch(ncpdpEndpoint, {
method: 'POST',
//mode: 'no-cors',
headers: {
diff --git a/src/components/RequestDashboard/Communication.jsx b/src/components/RequestDashboard/Communication.jsx
new file mode 100644
index 0000000..05de736
--- /dev/null
+++ b/src/components/RequestDashboard/Communication.jsx
@@ -0,0 +1,44 @@
+import { Button, Grid } from '@mui/material';
+import DeleteIcon from '@mui/icons-material/Delete';
+import useStyles from './styles';
+
+const Communication = props => {
+ const classes = useStyles();
+ const { communication, deleteCommunication } = props;
+
+ const convertTimeStamp = timeStamp => {
+ const date = new Date(timeStamp);
+ return date.toLocaleString();
+ };
+
+ return (
+
+
+
+ {`ID: ${communication.id}`}
+
+
+ {`Received: ${convertTimeStamp(communication.received)}`}
+
+
+ }
+ onClick={() => {
+ deleteCommunication(communication.id);
+ }}
+ >
+ Clear
+
+
+
+ {communication.payload[0].contentString}
+
+
+
+ );
+};
+
+export default Communication;
diff --git a/src/components/RequestDashboard/CommunicationsDialog.jsx b/src/components/RequestDashboard/CommunicationsDialog.jsx
new file mode 100644
index 0000000..cfe387a
--- /dev/null
+++ b/src/components/RequestDashboard/CommunicationsDialog.jsx
@@ -0,0 +1,143 @@
+import { useEffect, useState } from 'react';
+
+import { Button, Grid } from '@mui/material';
+import NotificationsIcon from '@mui/icons-material/Notifications';
+import Badge from '@mui/material/Badge';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import { Refresh } from '@mui/icons-material';
+
+import { styled } from '@mui/material/styles';
+import Paper from '@mui/material/Paper';
+import Communication from './Communication';
+
+const CommunicationsDialog = props => {
+ const { client, token } = props;
+ const [state, setState] = useState({
+ client: client,
+ token: token,
+ initialLoad: true,
+ communicationCount: 0,
+ communications: [],
+ open: false
+ });
+
+ const debugLog = message => {
+ console.log('CommunicationsDialog: ' + message);
+ };
+
+ useEffect(() => {
+ // reload on page load and dialog open
+ if (state.initialLoad) {
+ setState(prevState => ({ ...prevState, initialLoad: false }));
+ getCommunications();
+ }
+
+ const interval = setInterval(() => {
+ // page load...
+ getCommunications();
+ }, 1000 * 5); // reload every 5 seconds
+
+ return () => clearInterval(interval);
+ });
+
+ const getCommunications = () => {
+ if (state.client) {
+ // try to read communications from FHIR server
+ state.client
+ .request(`Communication?recipient=${props.token?.userId}`, {
+ graph: false,
+ flat: true
+ })
+ .then(bundle => {
+ loadCommunications(bundle);
+ });
+ }
+ };
+
+ const deleteCommunication = id => {
+ debugLog('deleteCommunication: ' + id);
+ if (id) {
+ state.client.delete(`Communication/${id}`).then(() => {
+ debugLog(`Deleted communication: ${id}`);
+ getCommunications();
+ });
+ }
+ };
+
+ const loadCommunications = bundle => {
+ let count = bundle.length;
+ setState(prevState => ({ ...prevState, communicationCount: count, communications: bundle }));
+ };
+
+ const handleClose = () => {
+ setState(prevState => ({ ...prevState, open: false }));
+ };
+
+ const Item = styled(Paper)(({ theme }) => ({
+ backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#EDF6FF',
+ ...theme.typography.body2,
+ padding: theme.spacing(1),
+ textAlign: 'left',
+ color: theme.palette.text.secondary
+ }));
+
+ const renderCommunications = () => {
+ return (
+
+ {state.communications.map(communication => {
+ return (
+
+ -
+
+
+
+ );
+ })}
+
+ );
+ };
+
+ return (
+
+ {
+ setState(prevState => ({ ...prevState, open: true, initialLoad: true }));
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ Communications ({state.communicationCount})
+
+
+ }
+ onClick={() => {
+ getCommunications();
+ }}
+ >
+ Refresh
+
+
+
+
+
+ {renderCommunications()}
+
+
+ );
+};
+
+export default CommunicationsDialog;
diff --git a/src/components/RequestDashboard/Home.jsx b/src/components/RequestDashboard/Home.jsx
index da8a9b0..ba322ec 100644
--- a/src/components/RequestDashboard/Home.jsx
+++ b/src/components/RequestDashboard/Home.jsx
@@ -6,6 +6,7 @@ import SettingsIcon from '@mui/icons-material/Settings';
import AccountBoxIcon from '@mui/icons-material/AccountBox';
import MedicalServicesIcon from '@mui/icons-material/MedicalServices';
+import CommunicationsDialog from './CommunicationsDialog';
import useStyles from './styles';
import PatientSection from './PatientSection';
import SettingsSection from './SettingsSection';
@@ -82,8 +83,10 @@ const Home = props => {
{/* spacer */}
{/** */}
{section ? (
-
+
+
+
{token.name}
Logout
@@ -115,7 +118,7 @@ const Home = props => {
-
+
);
diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx
index e1a031e..15bc014 100644
--- a/src/components/RequestDashboard/SettingsSection.jsx
+++ b/src/components/RequestDashboard/SettingsSection.jsx
@@ -1,4 +1,4 @@
-import React, { memo, useEffect } from 'react';
+import React, { memo, useEffect, useRef } from 'react';
import {
Button,
Checkbox,
@@ -33,15 +33,20 @@ import {
ENCOUNTER_START,
REMS_ETASU
} from '../../util/data';
+import { getPatientFirstAndLastName } from '../../util/util';
import { actionTypes } from '../../containers/ContextProvider/reducer';
import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider';
const ENDPOINT = [ORDER_SIGN, ORDER_SELECT, PATIENT_VIEW, ENCOUNTER_START, REMS_ETASU];
const SettingsSection = props => {
+ const { client, userId } = props;
+
const [state, dispatch, updateSetting, readSettings, saveSettings] =
React.useContext(SettingsContext);
+ const lastPacioTimestamp = useRef(null);
+
const fieldHeaders = Object.keys(headerDefinitions)
.map(key => ({ ...headerDefinitions[key], key }))
// Display the fields in descending order of type. If two fields are the same type,
@@ -95,35 +100,62 @@ const SettingsSection = props => {
});
};
- const clearResource =
- ({ ehrUrl, access_token }, type) =>
- () => {
- console.log('Clear ' + type + 's from the EHR: ' + ehrUrl);
- const client = FHIR.client({
- serverUrl: ehrUrl,
- ...(access_token ? { tokenResponse: access_token } : {})
- });
- client
- .request(type, { flat: true })
- .then(result => {
- console.log(result);
- result.forEach(resource => {
- console.log('Delete ' + type + ': ' + resource.id);
- client
- .delete(type + '/' + resource.id)
- .then(result => {
- console.log(result);
- })
- .catch(e => {
- console.log('Failed to delete ' + type + ' ' + resource.id);
- console.log(e);
- });
- });
- })
- .catch(e => {
- console.log('Failed to retrieve list of ' + type + 's');
+ const clearResourceWithParams = async (type, params) => {
+ console.log('Clear ' + type + 's from the EHR');
+ let query = type;
+ if (params != '') {
+ query = type + params;
+ console.log(' -> with params ' + params);
+ }
+
+ try {
+ const result = await client.request(query, { flat: true });
+ console.log(result);
+
+ // Delete all resources in parallel and wait for all to complete
+ const deletePromises = result.map(async resource => {
+ console.log('Delete ' + type + ': ' + resource.id);
+ try {
+ const deleteResult = await client.delete(type + '/' + resource.id);
+ console.log(deleteResult);
+ } catch (e) {
+ console.log('Failed to delete ' + type + ' ' + resource.id);
console.log(e);
- });
+ }
+ });
+
+ await Promise.all(deletePromises);
+ console.log(`Finished deleting all ${type}s`);
+ } catch (e) {
+ console.log('Failed to retrieve list of ' + type + 's');
+ console.log(e);
+ }
+ };
+
+ const clearResource =
+ ({}, type) =>
+ async () => {
+ // Delete all resources of type type
+ return await clearResourceWithParams(type, '');
+ };
+
+ const clearPatient =
+ ({ patientOfInterest }) =>
+ async () => {
+ console.log(`clear patient ${patientOfInterest}`);
+
+ try {
+ // Delete related resources first
+ await clearResourceWithParams('MedicationRequest', `?subject=${patientOfInterest}`);
+ await clearResourceWithParams('Communication', `?subject=${patientOfInterest}`);
+ await clearResourceWithParams('Coverage', `?beneficiary=${patientOfInterest}`);
+
+ // Finally delete the patient
+ const result = await client.delete(`Patient/${patientOfInterest}`);
+ console.log('Successfully deleted patient:', result);
+ } catch (e) {
+ console.log('Failed to delete patient:', e);
+ }
};
const reconnectEhr =
@@ -137,6 +169,295 @@ const SettingsSection = props => {
});
};
+ const createCommunication = (patientId, practitionerId, message) => {
+ const ts = Date.now();
+ const currentDate = new Date(ts);
+
+ const communication = {
+ resourceType: 'Communication',
+ status: 'in-progress',
+ category: [
+ {
+ coding: [
+ {
+ system: 'http://acme.org/messagetypes',
+ code: 'Alert'
+ }
+ ],
+ text: 'Alert'
+ }
+ ],
+ subject: {
+ reference: 'Patient/' + patientId
+ },
+ sent: currentDate.toISOString(),
+ received: currentDate.toISOString(),
+ recipient: [
+ {
+ reference: 'Practitioner/' + practitionerId
+ }
+ ],
+ sender: {
+ reference: 'Device/f001'
+ },
+ payload: [
+ {
+ contentString: message
+ }
+ ]
+ };
+
+ return communication;
+ };
+
+ const addCommunication = (patientId, message) => {
+ // add a communication notifying the practitioner that the resources were created
+ const communication = createCommunication(patientId, userId, message);
+
+ client
+ .create(communication)
+ .then(result => {
+ //console.log(result);
+ })
+ .catch(e => {
+ console.log('Failed to add Communication to EHR');
+ console.log(e);
+ });
+ };
+
+ const parsePacioToc = async pacioToc => {
+ console.log(' Parse PACIO TOC Bundle');
+ let medicationStatementList = [];
+ let patient = null;
+
+ pacioToc?.entry.forEach(tocEntry => {
+ const tocResource = tocEntry?.resource;
+
+ switch (tocResource?.resourceType) {
+ case 'Patient':
+ // find the patient
+ console.log(' Found Patient');
+ patient = tocResource;
+ break;
+ case 'Bundle':
+ console.log(' Process TOC Bundle');
+ tocResource?.entry.forEach(bundleEntry => {
+ const bundleEntryResource = bundleEntry?.resource;
+ switch (bundleEntryResource?.resourceType) {
+ case 'MedicationStatement':
+ // find the MedicationStatements
+ console.log(' Found MedicationStatement');
+ medicationStatementList.push(bundleEntryResource);
+ break;
+ }
+ });
+ break;
+ }
+ });
+
+ if (!patient) {
+ console.log('PACIO TOC missing Patient');
+ return;
+ }
+
+ // Get prescriber from settings
+ const newPrescriberId = state.pacioNewPrescriberId;
+ console.log(
+ ` Converting MedicationStatements to MedicationRequests with prescriber: ${newPrescriberId}`
+ );
+
+ let targetPatient = null;
+ console.log(' Creating new patient from TOC bundle');
+ try {
+ targetPatient = await client.create(patient);
+ console.log(
+ ` Created new patient: ${getPatientFirstAndLastName(targetPatient)} (${targetPatient.id})`
+ );
+ addCommunication(
+ targetPatient.id,
+ `Added new patient (${getPatientFirstAndLastName(targetPatient)}) from transfer of care`
+ );
+ } catch (e) {
+ console.log(' Failed to create patient:', e);
+ return;
+ }
+
+ // Convert MedicationStatements to MedicationRequests
+ const patientId = targetPatient.id;
+ const patientName = getPatientFirstAndLastName(targetPatient);
+ console.log(` Creating MedicationRequests for patient ${patientName} (${patientId})`);
+
+ for (const medicationStatement of medicationStatementList) {
+ // Create a new MedicationRequest from the MedicationStatement
+ const medicationRequest = {
+ resourceType: 'MedicationRequest',
+ meta: {
+ profile: ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest']
+ },
+ status: 'active',
+ intent: 'order',
+ subject: {
+ reference: `Patient/${patientId}`,
+ display: patientName
+ },
+ authoredOn: new Date().toISOString().split('T')[0],
+ requester: {
+ reference: `Practitioner/${newPrescriberId}`,
+ display: 'Transfer Prescriber'
+ }
+ };
+
+ // Copy medication information
+ if (medicationStatement.medicationCodeableConcept) {
+ medicationRequest.medicationCodeableConcept = medicationStatement.medicationCodeableConcept;
+ } else if (medicationStatement.medicationReference) {
+ medicationRequest.medicationReference = medicationStatement.medicationReference;
+ }
+
+ // Copy dosage if available
+ if (medicationStatement.dosage && medicationStatement.dosage.length > 0) {
+ medicationRequest.dosageInstruction = medicationStatement.dosage.map((dosage, index) => ({
+ sequence: index + 1,
+ text: dosage.text,
+ timing: dosage.timing,
+ route: dosage.route,
+ doseAndRate: dosage.doseAndRate
+ }));
+ }
+
+ // Add note about transfer
+ medicationRequest.note = [
+ {
+ text: `Continued from previous care. Original medication statement: ${medicationStatement.id || 'unknown'}`
+ }
+ ];
+
+ const medName =
+ medicationRequest.medicationCodeableConcept?.coding?.[0]?.display || 'Unknown medication';
+
+ try {
+ const medicationRequestResult = await client.create(medicationRequest);
+ console.log(` Added new MedicationRequest for ${medName} (ID: ${medicationRequestResult.id})`);
+ addCommunication(
+ patientId,
+ `Added new MedicationRequest for ${medName} from transfer of care`
+ );
+ } catch (e) {
+ console.log(` Failed to add MedicationRequest for ${medName}:`, e);
+ }
+ }
+ };
+
+ const pollPacioNotifications = async () => {
+ const pacioEhrUrl = state.pacioEhrUrl;
+
+ if (!pacioEhrUrl) {
+ return;
+ }
+
+ try {
+ console.log('Polling PACIO discharge notifications...');
+
+ // Build query URL
+ let query = '?type=message&_format=json';
+ if (lastPacioTimestamp.current) {
+ query += `&_lastUpdated=gt${lastPacioTimestamp.current}`;
+ console.log(` Using timestamp filter: ${lastPacioTimestamp.current}`);
+ } else {
+ console.log(' First poll - no timestamp filter, skipping saving old messages');
+ }
+
+ const pacioFhirClient = FHIR.client({
+ serverUrl: pacioEhrUrl
+ });
+
+ // Fetch discharge notifications
+ const searchBundle = await pacioFhirClient.request(query);
+
+ // ignore the first message, don't save old bundles
+ if (lastPacioTimestamp.current) {
+ if (searchBundle?.entry && searchBundle.entry.length > 0) {
+ console.log(` Found ${searchBundle.entry.length} discharge notification(s)`);
+
+ for (const notificationEntry of searchBundle.entry) {
+ const notificationBundle = notificationEntry.resource;
+
+ if (
+ notificationBundle?.resourceType === 'Bundle' &&
+ notificationBundle.type === 'message'
+ ) {
+ console.log(` Processing notification bundle: ${notificationBundle.id}`);
+
+ // Find DocumentReference in the notification bundle
+ let documentReference = null;
+ if (notificationBundle.entry) {
+ for (const entry of notificationBundle.entry) {
+ if (entry.resource?.resourceType === 'DocumentReference') {
+ documentReference = entry.resource;
+ console.log(' Found DocumentReference (TOC)');
+ break;
+ }
+ }
+ }
+
+ if (documentReference?.content?.[0]?.attachment?.url) {
+ const tocBundleUrl = documentReference.content[0].attachment.url;
+ console.log(` Fetching TOC bundle from: ${tocBundleUrl}`);
+
+ // Fetch and process the TOC bundle
+ try {
+ const tocBundle = await pacioFhirClient.request(tocBundleUrl);
+ await parsePacioToc(tocBundle);
+ } catch (e) {
+ console.log(` Failed to fetch/process TOC bundle:`, e);
+ }
+ } else {
+ console.log(' No DocumentReference with TOC bundle URL found');
+ }
+ }
+ }
+ } else {
+ console.log(' No new notifications found');
+ }
+ }
+
+ // Update timestamp from server response for next poll
+ if (searchBundle?.meta?.lastUpdated) {
+ const serverTimestamp = searchBundle.meta.lastUpdated;
+ console.log(` Server timestamp: ${serverTimestamp}`);
+ lastPacioTimestamp.current = serverTimestamp;
+ }
+
+ } catch (e) {
+ console.log('Failed to poll PACIO notifications:', e);
+ }
+ };
+
+ // Reset timestamp when PACIO URL changes (switching servers)
+ useEffect(() => {
+ if (state.pacioEhrUrl) {
+ console.log('PACIO URL changed, resetting timestamp for fresh poll');
+ lastPacioTimestamp.current = null;
+ }
+ }, [state.pacioEhrUrl]);
+
+ // Setup automatic polling on component mount
+ useEffect(() => {
+ if (!state.pacioEhrUrl) {
+ return;
+ }
+
+ // Poll immediately on mount or URL change
+ pollPacioNotifications();
+
+ // Then poll every 30 seconds
+ const interval = setInterval(() => {
+ pollPacioNotifications();
+ }, 30000); // 30 seconds
+
+ return () => clearInterval(interval);
+ }, [state.pacioEhrUrl]);
+
const resetHeaderDefinitions = [
{
display: 'Reset PIMS Database',
@@ -166,6 +487,11 @@ const SettingsSection = props => {
reset: clearResource,
parameter: 'Task'
},
+ {
+ display: 'Clear EHR Patient',
+ key: 'clearPatient',
+ reset: clearPatient
+ },
{
display: 'Reconnect EHR',
key: 'reconnectEHR',
@@ -419,4 +745,4 @@ const SettingsSection = props => {
);
};
-export default memo(SettingsSection);
+export default memo(SettingsSection);
\ No newline at end of file
diff --git a/src/components/RequestDashboard/styles.jsx b/src/components/RequestDashboard/styles.jsx
index cc74b42..533ac2a 100644
--- a/src/components/RequestDashboard/styles.jsx
+++ b/src/components/RequestDashboard/styles.jsx
@@ -158,6 +158,19 @@ export default makeStyles(
borderColor: 'white !important',
marginRight: '5px !important',
marginLeft: '20px !important'
+ },
+ communicationHeader: {
+ fontSize: '12px',
+ color: '#777',
+ borderBottom: '1px solid #e3e3ef'
+ },
+ communicationDescription: {
+ fontSize: '18px',
+ padding: '8px 0px 10px 2px'
+ },
+ communicationHeaderButton: {
+ height: '40px',
+ borderBottom: '1px solid #e3e3ef'
}
}),
diff --git a/src/components/SMARTBox/PatientBox.jsx b/src/components/SMARTBox/PatientBox.jsx
index a0d2358..c5ebe9d 100644
--- a/src/components/SMARTBox/PatientBox.jsx
+++ b/src/components/SMARTBox/PatientBox.jsx
@@ -20,7 +20,7 @@ import {
const PatientBox = props => {
const [globalState] = useContext(SettingsContext);
-
+
const [state, setState] = useState({
request: '',
deviceRequests: {},
diff --git a/src/util/buildScript.2017071.js b/src/util/buildScript.2017071.js
index 1ff52a9..e14ccef 100644
--- a/src/util/buildScript.2017071.js
+++ b/src/util/buildScript.2017071.js
@@ -121,7 +121,7 @@ function quantityUnitOfMeasureFromDrugFormCode(dispenseRequest) {
// https://terminology.hl7.org/5.0.0/CodeSystem-v3-orderableDrugForm.html
// Return NCPDP QuantityUnitOfMeasure
if (
- dispenseRequest.quantity.system.toLowerCase().endsWith('v3-orderableDrugForm'.toLowerCase())
+ dispenseRequest?.quantity?.system?.toLowerCase().endsWith('v3-orderableDrugForm'.toLowerCase())
) {
// is a subset of the codes, not a complete list
switch (dispenseRequest.quantity.code.toUpperCase()) {
@@ -253,19 +253,21 @@ function buildNewRxMedication(doc, medicationRequestResource) {
medicationPrescribed.appendChild(drugCoded);
// Medication Quantity
+ console.log(medicationRequestResource);
const dispenseRequest = medicationRequestResource.dispenseRequest;
- var quantity = doc.createElement('Quantity');
- xmlAddTextNode(doc, quantity, 'Value', dispenseRequest.quantity.value);
- xmlAddTextNode(doc, quantity, 'CodeListQualifier', 38); // Original Quantity
- var quantityUnitOfMeasure = doc.createElement('QuantityUnitOfMeasure');
- xmlAddTextNode(
- doc,
- quantityUnitOfMeasure,
- 'Code',
- quantityUnitOfMeasureFromDrugFormCode(dispenseRequest)
- );
- quantity.appendChild(quantityUnitOfMeasure);
- medicationPrescribed.appendChild(quantity);
+ var quantity = doc.createElement('Quantity');
+ xmlAddTextNode(doc, quantity, 'Value', dispenseRequest?.quantity?.value ? dispenseRequest?.quantity?.value : '');
+ xmlAddTextNode(doc, quantity, 'CodeListQualifier', 38); // Original Quantity
+ var quantityUnitOfMeasure = doc.createElement('QuantityUnitOfMeasure');
+ xmlAddTextNode(
+ doc,
+ quantityUnitOfMeasure,
+ 'Code',
+ quantityUnitOfMeasureFromDrugFormCode(dispenseRequest ? dispenseRequest : {})
+ );
+ quantity.appendChild(quantityUnitOfMeasure);
+ medicationPrescribed.appendChild(quantity);
+
// Medication Written Date
var writtenDate = doc.createElement('WrittenDate');
@@ -280,13 +282,17 @@ function buildNewRxMedication(doc, medicationRequestResource) {
doc,
medicationPrescribed,
'NumberOfRefills',
- dispenseRequest.numberOfRepeatsAllowed
+ dispenseRequest?.numberOfRepeatsAllowed ? dispenseRequest?.numberOfRepeatsAllowed : ''
);
+
// Medication Sig
- var sig = doc.createElement('Sig');
- xmlAddTextNode(doc, sig, 'SigText', medicationRequestResource.dosageInstruction[0].text);
- medicationPrescribed.appendChild(sig);
+ var dosageInstruction = medicationRequestResource?.dosageInstruction;
+ var dosageText = dosageInstruction ? dosageInstruction [0]?.text : ''
+ var sig = doc.createElement('Sig');
+ xmlAddTextNode(doc, sig, 'SigText', dosageText);
+ medicationPrescribed.appendChild(sig);
+
// Medication REMS
// A - Prescriber has checked REMS and the prescriber's actions have been completed.
diff --git a/src/util/data.js b/src/util/data.js
index 910383c..3204719 100644
--- a/src/util/data.js
+++ b/src/util/data.js
@@ -16,6 +16,16 @@ const headerDefinitions = {
type: 'check',
default: env.get('VITE_USE_INTERMEDIARY').asBool()
},
+ usePharmacyIntermediary: {
+ type: 'check',
+ display: 'Use Pharmacy Intermediary (NCPDP NewRx)',
+ default: env.get('VITE_USE_PHARMACY_INTERMEDIARY').asBool() || false
+ },
+ pharmacyIntermediaryUrl: {
+ type: 'input',
+ display: 'Pharmacy Intermediary URL (NCPDP Endpoint)',
+ default: env.get('VITE_PHARMACY_INTERMEDIARY').asString() || 'http://localhost:3003/ncpdp/script'
+ },
alternativeTherapy: {
display: 'Alternative Therapy Cards Allowed',
type: 'check',
@@ -101,7 +111,20 @@ const headerDefinitions = {
type: 'check',
default: false
},
-
+ pacioEhrUrl: {
+ display: 'PACIO EHR Base URL',
+ type: 'input',
+ default: env.get('VITE_PACIO_EHR_URL').asString()
+ },
+ pacioNewPrescriberId: {
+ display: 'PACIO New Prescriber ID',
+ type: 'input',
+ default: env.get('VITE_PACIO_NEW_PRESCRIBER_ID').asString()
+ },
+ patientOfInterest: {
+ display: 'Patient ID (for Clear)',
+ type: 'input'
+ },
hookToSend: {
display: 'Send hook on patient select',
type: 'dropdown',
@@ -349,4 +372,4 @@ export {
REMS_ETASU,
CDS_SERVICE,
serviceEndpoints
-};
+};
\ No newline at end of file