From 89bfc8bf8f04b0a6b12943dc907d0dc5525e788e Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 12 Sep 2025 12:10:11 -0400 Subject: [PATCH 1/9] update prefetch with pharmacy ref --- src/PrefetchTemplate.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 50b8e9eb..4ed50813 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -11,6 +11,8 @@ export class PrefetchTemplate { ); const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}'); + const PHARMACY_PREFETCH = new PrefetchTemplate('Organization/pharm0111'); + const ALL_REQUESTS_PREFETCH = new PrefetchTemplate( 'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication' ); @@ -19,6 +21,7 @@ export class PrefetchTemplate { prefetchMap.set('request', REQUEST_PREFETCH); prefetchMap.set('practitioner', PRACTITIONER_PREFETCH); prefetchMap.set('patient', PATIENT_PREFETCH); + prefetchMap.set('pharmacy', PHARMACY_PREFETCH); prefetchMap.set('medicationRequests', ALL_REQUESTS_PREFETCH); // prefetchMap.set("ServiceRequest", SERVICE_REQUEST_BUNDLE); // prefetchMap.set("Encounter", ENCOUNTER_BUNDLE); @@ -44,6 +47,8 @@ export class PrefetchTemplate { paramElementMap.set('context.draftOrders.context.appointments.Appointment.id', ['id']); paramElementMap.set('context.draftOrders.context.encounterId', ['id']); paramElementMap.set('context.patientId', ['subject', 'reference']); + paramElementMap.set('context.pharmacyId', ['id']); + return paramElementMap; } From 8a751ae96ef3991fd467aa677c02a51bb55584e7 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 12 Sep 2025 15:27:58 -0400 Subject: [PATCH 2/9] fix the prefetch bug --- src/PrefetchTemplate.js | 30 +++++++++++++++----------- src/components/SMARTBox/PatientBox.jsx | 22 +++++++++++++++---- src/containers/RequestBuilder.jsx | 4 +++- src/util/buildRequest.js | 7 ++++-- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 4ed50813..65e2674e 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -11,7 +11,7 @@ export class PrefetchTemplate { ); const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}'); - const PHARMACY_PREFETCH = new PrefetchTemplate('Organization/pharm0111'); + const PHARMACY_PREFETCH = new PrefetchTemplate('Organization/{{context.pharmacyId}}'); const ALL_REQUESTS_PREFETCH = new PrefetchTemplate( 'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication' @@ -52,32 +52,38 @@ export class PrefetchTemplate { return paramElementMap; } - static generateQueries(requestBundle, patientReference, userReference, ...prefetchKeys) { - var resolvedQueries = new Map(); - for (var i = 0; i < prefetchKeys.length; i++) { - var prefetchKey = prefetchKeys[i]; - var query = prefetchMap.get(prefetchKey).getQuery(); + static generateQueries(requestBundle, patientReference, userReference, pharmacyId, ...prefetchKeys) { + var resolvedQueries = new Map(); + for (var i = 0; i < prefetchKeys.length; i++) { + var prefetchKey = prefetchKeys[i]; + var query = prefetchMap.get(prefetchKey).getQuery(); // Regex source: https://regexland.com/all-between-specified-characters/ - var parametersToFill = query.match(/(?<={{).*?(?=}})/gs); - var resolvedQuery = query.slice(); + var parametersToFill = query.match(/(?<={{).*?(?=}})/gs); + var resolvedQuery = query.slice(); for (var j = 0; j < parametersToFill.length; j++) { var unresolvedParameter = parametersToFill[j]; var resolvedParameter; if (requestBundle) { - resolvedParameter = PrefetchTemplate.resolveParameter(unresolvedParameter, requestBundle); + if (unresolvedParameter === 'context.pharmacyId') { + resolvedParameter = pharmacyId; + } else { + resolvedParameter = PrefetchTemplate.resolveParameter(unresolvedParameter, requestBundle); + } } else { if (unresolvedParameter === 'context.patientId') { resolvedParameter = patientReference; } else if (unresolvedParameter === 'context.userId') { resolvedParameter = userReference; + } else if (unresolvedParameter === 'context.pharmacyId') { + resolvedParameter = pharmacyId; } } resolvedQuery = resolvedQuery.replace('{{' + unresolvedParameter + '}}', resolvedParameter); } - resolvedQueries.set(prefetchKey, resolvedQuery); - } - return resolvedQueries; + resolvedQueries.set(prefetchKey, resolvedQuery); } + return resolvedQueries; +} // Source: https://www.tutorialspoint.com/accessing-nested-javascript-objects-with-string-key static getProp(object, path) { diff --git a/src/components/SMARTBox/PatientBox.jsx b/src/components/SMARTBox/PatientBox.jsx index 9b4e4edc..190f4c8e 100644 --- a/src/components/SMARTBox/PatientBox.jsx +++ b/src/components/SMARTBox/PatientBox.jsx @@ -167,17 +167,27 @@ const PatientBox = props => { const fetchResources = queries => { let requests = []; callback('prefetchCompleted', false); + console.log('🔍 PHARMACY DEBUG - All queries to fetch:', queries); // ADD THIS + queries.forEach((query, queryKey) => { + console.log(`🔍 PHARMACY DEBUG - Processing ${queryKey}: ${query}`); // ADD THIS const urlQuery = '/' + query; requests.push( client .request(urlQuery) .then(response => { - console.log(response); + console.log(`🔍 PHARMACY DEBUG - SUCCESS for ${queryKey}:`, response); // ADD THIS return response; }) + .catch(error => { + console.log(`🔍 PHARMACY DEBUG - FAILED for ${queryKey}:`, error); // ADD THIS + return null; // Return null so Promise.all doesn't fail completely + }) .then(resource => { - callbackMap('prefetchedResources', queryKey, resource); + if (resource) { + console.log(`🔍 PHARMACY DEBUG - Calling callbackMap for ${queryKey}`); // ADD THIS + callbackMap('prefetchedResources', queryKey, resource); + } }) ); }); @@ -202,9 +212,11 @@ const PatientBox = props => { request, patientReference, userReference, + 'pharm0111', 'request', 'patient', - 'practitioner' + 'practitioner', + 'pharmacy' ); fetchResources(queries); @@ -218,9 +230,11 @@ const PatientBox = props => { request, patientReference, userReference, + 'pharm0111', 'patient', 'practitioner', - 'medicationRequests' + 'medicationRequests', + 'pharmacy' ); fetchResources(queries); } diff --git a/src/containers/RequestBuilder.jsx b/src/containers/RequestBuilder.jsx index d7117fc4..0e6355f6 100644 --- a/src/containers/RequestBuilder.jsx +++ b/src/containers/RequestBuilder.jsx @@ -48,6 +48,7 @@ const RequestBuilder = props => { const displayRequestBox = !!globalState.patient?.id; useEffect(() => { + console.log('Prefetched Resources updated:'); console.log(state.prefetchedResources); }, [state.prefetchedResources]); @@ -181,7 +182,8 @@ const RequestBuilder = props => { prefetch, globalState.sendPrefetch, hook, - hookConfig + hookConfig, + 'pharm0111' ); let baseUrl = globalState.baseUrl; diff --git a/src/util/buildRequest.js b/src/util/buildRequest.js index c0397a07..1e36e8dd 100644 --- a/src/util/buildRequest.js +++ b/src/util/buildRequest.js @@ -9,7 +9,9 @@ export default function buildRequest( prefetch, includePrefetch, hook, - hookConfig + hookConfig, + pharmacyId = 'pharm0111' + ) { // Use the provided user if there is no request for this hook let userId = 'Practitioner/' + user; @@ -31,7 +33,8 @@ export default function buildRequest( context: { userId: userId, patientId: patient.id, - encounterId: 'enc89284' + encounterId: 'enc89284', + pharmacyId: pharmacyId } }; From 96e00d0516fea05d8bea9ae66c731504baaa0a8b Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 10:24:16 -0400 Subject: [PATCH 3/9] update request generator to use healthcareservice for pharmacy id --- src/PrefetchTemplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 65e2674e..ec11be84 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -11,7 +11,7 @@ export class PrefetchTemplate { ); const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}'); - const PHARMACY_PREFETCH = new PrefetchTemplate('Organization/{{context.pharmacyId}}'); + const PHARMACY_PREFETCH = new PrefetchTemplate('HealthcareService/{{context.pharmacyId}}'); const ALL_REQUESTS_PREFETCH = new PrefetchTemplate( 'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication' From e2974e23a1216eb4e01e58619f32af2565256ea7 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 14:55:26 -0400 Subject: [PATCH 4/9] fix the context issue --- .env | 1 + README.md | 1 + src/PrefetchTemplate.js | 31 ++++++++++++++----------------- src/util/data.js | 5 +++++ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.env b/.env index c6757a9d..6bcb2d09 100644 --- a/.env +++ b/.env @@ -26,5 +26,6 @@ VITE_USER = alice VITE_HOOK_TO_SEND = patient-view VITE_URL_FILTER = http://localhost:3000/* VITE_USE_INTERMEDIARY = false +VITE_USE_PHARMACY_IN_PREFETCH = true VITE_INTERMEDIARY = http://localhost:3003 VITE_DISABLE_MEDICATION_STATUS = false diff --git a/README.md b/README.md index 7604fe70..49d36a5c 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ Following are a list of modifiable paths: | VITE_URL | `http://localhost:3000` | The base url of this app. Should be modified if the port or domain change. | | VITE_USER | `alice` | The default user to login as when opening the app. | | 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_USE_PHARMACY_IN_PREFETCH | true | When true, the app will send pharmacy information to the rems admin in the CDS Hooks prefetch | | VITE_INTERMEDIARY | `http://localhost:3030` | The base url of the intermediary. | # Data Rights diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index ec11be84..2577465c 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -1,7 +1,7 @@ // Prefetch Template Source: // https://build.fhir.org/ig/HL7/davinci-crd/hooks.html#prefetch export class PrefetchTemplate { - static generatePrefetchMap() { +static generatePrefetchMap(includePharmacy = true, pharmacyId = 'pharm0111') { const prefetchMap = new Map(); const PRACTITIONER_PREFETCH = new PrefetchTemplate('{{context.userId}}'); @@ -11,20 +11,21 @@ export class PrefetchTemplate { ); const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}'); - const PHARMACY_PREFETCH = new PrefetchTemplate('HealthcareService/{{context.pharmacyId}}'); - const ALL_REQUESTS_PREFETCH = new PrefetchTemplate( 'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication' ); - // prefetchMap.set("Coverage", COVERAGE_PREFETCH_QUERY); + // Core prefetch items (always included) prefetchMap.set('request', REQUEST_PREFETCH); prefetchMap.set('practitioner', PRACTITIONER_PREFETCH); prefetchMap.set('patient', PATIENT_PREFETCH); - prefetchMap.set('pharmacy', PHARMACY_PREFETCH); prefetchMap.set('medicationRequests', ALL_REQUESTS_PREFETCH); - // prefetchMap.set("ServiceRequest", SERVICE_REQUEST_BUNDLE); - // prefetchMap.set("Encounter", ENCOUNTER_BUNDLE); + + // Optional pharmacy prefetch (only if explicitly requested and pharmacyId provided) + if (includePharmacy && pharmacyId) { + const PHARMACY_PREFETCH = new PrefetchTemplate(`HealthcareService/${pharmacyId}`); + prefetchMap.set('pharmacy', PHARMACY_PREFETCH); + } return prefetchMap; } @@ -47,8 +48,6 @@ export class PrefetchTemplate { paramElementMap.set('context.draftOrders.context.appointments.Appointment.id', ['id']); paramElementMap.set('context.draftOrders.context.encounterId', ['id']); paramElementMap.set('context.patientId', ['subject', 'reference']); - paramElementMap.set('context.pharmacyId', ['id']); - return paramElementMap; } @@ -60,26 +59,24 @@ 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]; var resolvedParameter; if (requestBundle) { - if (unresolvedParameter === 'context.pharmacyId') { - resolvedParameter = pharmacyId; - } else { resolvedParameter = PrefetchTemplate.resolveParameter(unresolvedParameter, requestBundle); - } } else { if (unresolvedParameter === 'context.patientId') { resolvedParameter = patientReference; } else if (unresolvedParameter === 'context.userId') { resolvedParameter = userReference; - } else if (unresolvedParameter === 'context.pharmacyId') { - resolvedParameter = pharmacyId; - } + } + } + if (resolvedParameter) { + resolvedQuery = resolvedQuery.replace('{{' + unresolvedParameter + '}}', resolvedParameter); } - resolvedQuery = resolvedQuery.replace('{{' + unresolvedParameter + '}}', resolvedParameter); } + } resolvedQueries.set(prefetchKey, resolvedQuery); } return resolvedQueries; diff --git a/src/util/data.js b/src/util/data.js index 819f044b..b0333c9d 100644 --- a/src/util/data.js +++ b/src/util/data.js @@ -1,6 +1,11 @@ import env from 'env-var'; const headerDefinitions = { + includePharmacyInPreFetch: { + display: 'Include Pharmacy in Prefetch', + type: 'check', + default: env.get('VITE_USE_PHARMACY_IN_PREFETCH').asBool() + }, useIntermediary: { display: 'Use Intermediary', type: 'check', From 3032323f00260f3a8e71db05a868dc9b899be593 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 15:06:06 -0400 Subject: [PATCH 5/9] remove pharmacy from context in request builder --- src/containers/RequestBuilder.jsx | 3 +-- src/util/buildRequest.js | 9 +++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/containers/RequestBuilder.jsx b/src/containers/RequestBuilder.jsx index 0e6355f6..ca9aa345 100644 --- a/src/containers/RequestBuilder.jsx +++ b/src/containers/RequestBuilder.jsx @@ -182,8 +182,7 @@ const RequestBuilder = props => { prefetch, globalState.sendPrefetch, hook, - hookConfig, - 'pharm0111' + hookConfig ); let baseUrl = globalState.baseUrl; diff --git a/src/util/buildRequest.js b/src/util/buildRequest.js index 1e36e8dd..2b67c72f 100644 --- a/src/util/buildRequest.js +++ b/src/util/buildRequest.js @@ -9,10 +9,8 @@ export default function buildRequest( prefetch, includePrefetch, hook, - hookConfig, - pharmacyId = 'pharm0111' - -) { + hookConfig + ) { // Use the provided user if there is no request for this hook let userId = 'Practitioner/' + user; if (request) { @@ -33,8 +31,7 @@ export default function buildRequest( context: { userId: userId, patientId: patient.id, - encounterId: 'enc89284', - pharmacyId: pharmacyId + encounterId: 'enc89284' } }; From 029dcbd90d155e6d122d7c993e5ab3fe3dacc980 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 15:08:10 -0400 Subject: [PATCH 6/9] run prettier/lint --- src/PrefetchTemplate.js | 64 +++++---- src/components/App.jsx | 18 ++- .../PatientSearchBar/PatientSearchBar.jsx | 43 +++--- src/components/RequestBox/RequestBox.jsx | 4 +- src/components/RequestDashboard/Home.jsx | 61 ++++----- .../RequestDashboard/PatientSection.jsx | 7 +- .../RequestDashboard/SettingsSection.jsx | 35 ++--- .../RequestDashboard/TasksSection.jsx | 55 ++++---- src/components/RequestDashboard/styles.jsx | 2 +- src/components/SMARTBox/PatientBox.jsx | 12 +- src/containers/BackOffice/BackOffice.jsx | 54 ++++---- src/containers/BackOffice/Dashboard.jsx | 127 +++++++++--------- src/containers/BackOffice/TaskTab.jsx | 59 ++++---- src/containers/BackOffice/styles.jsx | 99 +++++++------- .../ContextProvider/SettingsProvider.jsx | 9 +- src/containers/Gateway/Gateway.jsx | 14 +- src/containers/Index.jsx | 35 +++-- src/containers/PatientPortal.jsx | 16 ++- src/containers/RequestBuilder.jsx | 29 +++- src/util/auth.js | 8 +- src/util/buildRequest.js | 2 +- src/util/data.js | 79 ++++++++--- src/util/fhir.js | 2 +- 23 files changed, 487 insertions(+), 347 deletions(-) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 2577465c..1015a266 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -1,7 +1,7 @@ // Prefetch Template Source: // https://build.fhir.org/ig/HL7/davinci-crd/hooks.html#prefetch export class PrefetchTemplate { -static generatePrefetchMap(includePharmacy = true, pharmacyId = 'pharm0111') { + static generatePrefetchMap(includePharmacy = true, pharmacyId = 'pharm0111') { const prefetchMap = new Map(); const PRACTITIONER_PREFETCH = new PrefetchTemplate('{{context.userId}}'); @@ -51,36 +51,48 @@ static generatePrefetchMap(includePharmacy = true, pharmacyId = 'pharm0111') { return paramElementMap; } - static generateQueries(requestBundle, patientReference, userReference, pharmacyId, ...prefetchKeys) { - var resolvedQueries = new Map(); - for (var i = 0; i < prefetchKeys.length; i++) { - var prefetchKey = prefetchKeys[i]; - var query = prefetchMap.get(prefetchKey).getQuery(); + static generateQueries( + requestBundle, + patientReference, + userReference, + pharmacyId, + ...prefetchKeys + ) { + var resolvedQueries = new Map(); + for (var i = 0; i < prefetchKeys.length; i++) { + var prefetchKey = prefetchKeys[i]; + var query = prefetchMap.get(prefetchKey).getQuery(); // 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]; - var resolvedParameter; - if (requestBundle) { - resolvedParameter = PrefetchTemplate.resolveParameter(unresolvedParameter, requestBundle); - } else { - if (unresolvedParameter === 'context.patientId') { - resolvedParameter = patientReference; - } else if (unresolvedParameter === 'context.userId') { - resolvedParameter = userReference; - } - } - if (resolvedParameter) { - resolvedQuery = resolvedQuery.replace('{{' + unresolvedParameter + '}}', resolvedParameter); + var parametersToFill = query.match(/(?<={{).*?(?=}})/gs); + var resolvedQuery = query.slice(); + if (parametersToFill) { + for (var j = 0; j < parametersToFill.length; j++) { + var unresolvedParameter = parametersToFill[j]; + var resolvedParameter; + if (requestBundle) { + resolvedParameter = PrefetchTemplate.resolveParameter( + unresolvedParameter, + requestBundle + ); + } else { + if (unresolvedParameter === 'context.patientId') { + resolvedParameter = patientReference; + } else if (unresolvedParameter === 'context.userId') { + resolvedParameter = userReference; + } + } + if (resolvedParameter) { + resolvedQuery = resolvedQuery.replace( + '{{' + unresolvedParameter + '}}', + resolvedParameter + ); + } } } + resolvedQueries.set(prefetchKey, resolvedQuery); } - resolvedQueries.set(prefetchKey, resolvedQuery); + return resolvedQueries; } - return resolvedQueries; -} // Source: https://www.tutorialspoint.com/accessing-nested-javascript-objects-with-string-key static getProp(object, path) { diff --git a/src/components/App.jsx b/src/components/App.jsx index de99d55f..f24fc4bd 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -28,10 +28,24 @@ const App = () => { } /> - } /> + + + + } + /> } /> {/* forcibly enter backoffice workflow */} - } /> + + + + } + /> { - -

Filter patient list

- { - setInput(newInputValue.toLowerCase()); - }} - options={listOfPatients[0].map(item => item.name)} - renderInput={params => } - /> -

- Showing {getFilteredLength(input, listOfPatients)} of {props.searchablePatients.length}{' '} - records -

-
+ +

Filter patient list

+ { + setInput(newInputValue.toLowerCase()); + }} + options={listOfPatients[0].map(item => item.name)} + renderInput={params => } + /> +

+ Showing {getFilteredLength(input, listOfPatients)} of{' '} + {props.searchablePatients.length} records +

+
- diff --git a/src/components/RequestBox/RequestBox.jsx b/src/components/RequestBox/RequestBox.jsx index f9823241..c402427e 100644 --- a/src/components/RequestBox/RequestBox.jsx +++ b/src/components/RequestBox/RequestBox.jsx @@ -182,9 +182,7 @@ const RequestBox = props => { let userId = prefetchedResources?.practitioner?.id; if (!userId) { - console.log( - 'Practitioner not populated from prefetch, using user: ' + user - ); + console.log('Practitioner not populated from prefetch, using user: ' + user); userId = user; } diff --git a/src/components/RequestDashboard/Home.jsx b/src/components/RequestDashboard/Home.jsx index 9ce8648a..da8a9b03 100644 --- a/src/components/RequestDashboard/Home.jsx +++ b/src/components/RequestDashboard/Home.jsx @@ -64,36 +64,37 @@ const Home = props => { } return (
- - {section ? '' : } {/* spacer */} - {renderMainButton(patientButton, )} - {renderMainButton(taskButton, )} - {renderMainButton(settingsButton, )} - {section ? ( - - -   EHR Request Generator - - - ) : ( - - )} - {/* spacer */} - {/** */} - {section ? ( - - - {token.name} - - - - ) : ( - - )} - {/**/} - + + {section ? '' : } {/* spacer */} + {renderMainButton(patientButton, )} + {renderMainButton(taskButton, )} + {renderMainButton(settingsButton, )} + {section ? ( + + + +   EHR Request Generator + + + ) : ( + + )} + {/* spacer */} + {/** */} + {section ? ( + + + {token.name} + + + + ) : ( + + )} + {/**/} +
); }; diff --git a/src/components/RequestDashboard/PatientSection.jsx b/src/components/RequestDashboard/PatientSection.jsx index 0439e998..5eaeedf9 100644 --- a/src/components/RequestDashboard/PatientSection.jsx +++ b/src/components/RequestDashboard/PatientSection.jsx @@ -8,7 +8,12 @@ const PatientSection = props => { return (
{state.startup ? ( - + ) : ( <>Loading... )} diff --git a/src/components/RequestDashboard/SettingsSection.jsx b/src/components/RequestDashboard/SettingsSection.jsx index 4bd6efdc..e1a031ec 100644 --- a/src/components/RequestDashboard/SettingsSection.jsx +++ b/src/components/RequestDashboard/SettingsSection.jsx @@ -39,7 +39,8 @@ import { SettingsContext } from '../../containers/ContextProvider/SettingsProvid const ENDPOINT = [ORDER_SIGN, ORDER_SELECT, PATIENT_VIEW, ENCOUNTER_START, REMS_ETASU]; const SettingsSection = props => { - const [state, dispatch, updateSetting, readSettings, saveSettings] = React.useContext(SettingsContext); + const [state, dispatch, updateSetting, readSettings, saveSettings] = + React.useContext(SettingsContext); const fieldHeaders = Object.keys(headerDefinitions) .map(key => ({ ...headerDefinitions[key], key })) @@ -94,7 +95,7 @@ const SettingsSection = props => { }); }; - const clearResource = + const clearResource = ({ ehrUrl, access_token }, type) => () => { console.log('Clear ' + type + 's from the EHR: ' + ehrUrl); @@ -151,19 +152,19 @@ const SettingsSection = props => { display: 'Clear EHR In-Progress Forms', key: 'clearQuestionnaireResponses', reset: clearResource, - parameter: 'QuestionnaireResponse' + parameter: 'QuestionnaireResponse' }, { display: 'Clear EHR Dispense Statuses', key: 'clearMedicationDispenses', reset: clearResource, - parameter: 'MedicationDispense' + parameter: 'MedicationDispense' }, { display: 'Clear EHR Tasks', key: 'clearTasks', reset: clearResource, - parameter: 'Task' + parameter: 'Task' }, { display: 'Reconnect EHR', @@ -184,17 +185,19 @@ const SettingsSection = props => { case 'input': return ( - { ( (state['useDefaultUser'] && key === 'defaultUser') || key != 'defaultUser' ) ? ( -
- updateSetting(key, event.target.value)} - sx={{ width: '100%' }} - /> -
- ) : ('') } + {(state['useDefaultUser'] && key === 'defaultUser') || key != 'defaultUser' ? ( +
+ updateSetting(key, event.target.value)} + sx={{ width: '100%' }} + /> +
+ ) : ( + '' + )}
); case 'check': diff --git a/src/components/RequestDashboard/TasksSection.jsx b/src/components/RequestDashboard/TasksSection.jsx index a342bf61..7bf03159 100644 --- a/src/components/RequestDashboard/TasksSection.jsx +++ b/src/components/RequestDashboard/TasksSection.jsx @@ -28,7 +28,7 @@ const taskStatus = Object.freeze({ }); const TasksSection = props => { const classes = useStyles(); - const {client, userName, userId} = props; + const { client, userName, userId } = props; const [tasks, setTasks] = useState([]); const [state] = React.useContext(SettingsContext); const [value, setValue] = useState(0); @@ -64,7 +64,8 @@ const TasksSection = props => { assignTaskToDefaultPractitioner(taskClone); } else if (val === 'patient') { assignTaskToPatient(taskClone); - } else { //'unassign' + } else { + //'unassign' unassignTask(taskClone); } handleAssignMenuClose(); @@ -120,7 +121,7 @@ const TasksSection = props => { fetchTasks(); }); } - } + }; const assignTaskToRequester = task => { if (task) { task = washTask(task); @@ -129,7 +130,7 @@ const TasksSection = props => { // assign to requester if available if (task?.requester) { - user = task.requester?.reference + user = task.requester?.reference; } task.owner = { reference: user @@ -139,7 +140,7 @@ const TasksSection = props => { fetchTasks(); }); } - };; + }; const assignTaskToDefaultPractitioner = task => { if (task) { task = washTask(task); @@ -190,14 +191,16 @@ const TasksSection = props => { if (state.patient && state.patient.id) { identifier = `Task?patient=${state.patient.id}`; } - client.request(identifier, { resolveReferences: ['for', 'owner', 'requester'] }).then(request => { - console.log(request); - if (request && request.entry) { - setTasks(request.entry.map(e => e.resource)); - } else { - setTasks([]); - } - }); + client + .request(identifier, { resolveReferences: ['for', 'owner', 'requester'] }) + .then(request => { + console.log(request); + if (request && request.entry) { + setTasks(request.entry.map(e => e.resource)); + } else { + setTasks([]); + } + }); }; useEffect(() => { fetchTasks(); @@ -291,18 +294,20 @@ const TasksSection = props => { {assignOptions.map(({ id, display }) => { // only give the 'Assign to requester if the requester is available' - if (((id === 'me') && userId && userName) - || ((id === 'requester') && (anchorAssign?.task?.requester)) - || ((id === 'defaultPractitioner') && (!anchorAssign?.task?.requester)) - || (id != 'me') && (id != 'requester') && (id != 'defaultPractitioner')) { - return ( - { - handleChangeAssign(anchorAssign?.task, id); - }} - >{`${display}`} - ); + if ( + (id === 'me' && userId && userName) || + (id === 'requester' && anchorAssign?.task?.requester) || + (id === 'defaultPractitioner' && !anchorAssign?.task?.requester) || + (id != 'me' && id != 'requester' && id != 'defaultPractitioner') + ) { + return ( + { + handleChangeAssign(anchorAssign?.task, id); + }} + >{`${display}`} + ); } })} diff --git a/src/components/RequestDashboard/styles.jsx b/src/components/RequestDashboard/styles.jsx index 2eab4379..cc74b42e 100644 --- a/src/components/RequestDashboard/styles.jsx +++ b/src/components/RequestDashboard/styles.jsx @@ -157,7 +157,7 @@ export default makeStyles( color: 'white !important', borderColor: 'white !important', marginRight: '5px !important', - marginLeft: '20px !important', + marginLeft: '20px !important' } }), diff --git a/src/components/SMARTBox/PatientBox.jsx b/src/components/SMARTBox/PatientBox.jsx index 190f4c8e..b4c32b42 100644 --- a/src/components/SMARTBox/PatientBox.jsx +++ b/src/components/SMARTBox/PatientBox.jsx @@ -50,7 +50,7 @@ const PatientBox = props => { responseExpirationDays, request, launchUrl, - showButtons, + showButtons } = props; const medicationColumns = [ @@ -168,7 +168,7 @@ const PatientBox = props => { let requests = []; callback('prefetchCompleted', false); console.log('🔍 PHARMACY DEBUG - All queries to fetch:', queries); // ADD THIS - + queries.forEach((query, queryKey) => { console.log(`🔍 PHARMACY DEBUG - Processing ${queryKey}: ${query}`); // ADD THIS const urlQuery = '/' + query; @@ -627,7 +627,9 @@ const PatientBox = props => { ) - ) : ""} + ) : ( + '' + )} {props.showButtons ? ( state.showQuestionnaires ? ( diff --git a/src/containers/BackOffice/BackOffice.jsx b/src/containers/BackOffice/BackOffice.jsx index facd8b3f..0d3da7b2 100644 --- a/src/containers/BackOffice/BackOffice.jsx +++ b/src/containers/BackOffice/BackOffice.jsx @@ -10,7 +10,7 @@ import useStyles from './styles'; import { logout } from '../../util/auth'; -const BackOffice = (props) => { +const BackOffice = props => { const classes = useStyles(); const { client, token } = props; @@ -19,39 +19,35 @@ const BackOffice = (props) => { }, []); return ( - - { token && client ? ( - - -
- -
-
- -

EHR Back Office

+ {token && client ? ( + +
+ +
+
+ +

+ EHR Back Office{' '} +

+
+ + {token.name} + +
- - {token.name} - - -
- -
- - - + +
+ + ) : ( - ) } - + )} - - ); }; diff --git a/src/containers/BackOffice/Dashboard.jsx b/src/containers/BackOffice/Dashboard.jsx index 86cb9da5..a4792508 100644 --- a/src/containers/BackOffice/Dashboard.jsx +++ b/src/containers/BackOffice/Dashboard.jsx @@ -7,75 +7,74 @@ import { SettingsContext } from '../ContextProvider/SettingsProvider'; import TaskTab from './TaskTab'; function a11yProps(index) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}` - }; - } + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}` + }; +} export default function Dashboard(props) { - const { client, token } = props; - const [headerStyle, setHeaderStyle] = useState(undefined); - const [globalState, dispatch, updateSetting, readSettings] = React.useContext(SettingsContext); - console.log('global state patient -- > ', globalState.patient); + const { client, token } = props; + const [headerStyle, setHeaderStyle] = useState(undefined); + const [globalState, dispatch, updateSetting, readSettings] = React.useContext(SettingsContext); + console.log('global state patient -- > ', globalState.patient); + useEffect(() => { + readSettings(); + const updateScrollState = () => { + var threshold = 10; + if (window.scrollY > threshold) { + setHeaderStyle('true'); + } else { + setHeaderStyle(undefined); + } + }; + document.addEventListener('scroll', updateScrollState); + return () => document.removeEventListener('scroll', updateScrollState); + }, []); - useEffect(() => { - readSettings(); - const updateScrollState = () => { - var threshold = 10; - if (window.scrollY > threshold) { - setHeaderStyle("true"); - } else { - setHeaderStyle(undefined); - } - } - document.addEventListener("scroll", updateScrollState); - return () => document.removeEventListener("scroll", updateScrollState); - }, []); + const [tabIndex, setValue] = useState(0); - const [tabIndex, setValue] = useState(0); + const handleChange = (event, newValue) => { + setValue(newValue); + }; - const handleChange = (event, newValue) => { - setValue(newValue); - }; + return ( +
+ + + + + + + + - return ( -
- - - - - - - - - - - - {tabIndex === 0 && ( - - - - )} - {tabIndex === 1 && ( - - - - )} - - + + + {tabIndex === 0 && ( + + + + )} + {tabIndex === 1 && ( + + - -
- ); -} \ No newline at end of file + )} +
+ + +
+
+ ); +} diff --git a/src/containers/BackOffice/TaskTab.jsx b/src/containers/BackOffice/TaskTab.jsx index 5b8ef194..4db81f23 100644 --- a/src/containers/BackOffice/TaskTab.jsx +++ b/src/containers/BackOffice/TaskTab.jsx @@ -60,10 +60,14 @@ const TaskTab = props => { getPatients(); } // if use default user is set, use default user otherwise use logged in user if set - let currentUser = globalState.useDefaultUser ? globalState.defaultUser : (token.userId ? token.userId : globalState.defaultUser); - setState(prevState => ({...prevState, user: currentUser})); + let currentUser = globalState.useDefaultUser + ? globalState.defaultUser + : token.userId + ? token.userId + : globalState.defaultUser; + setState(prevState => ({ ...prevState, user: currentUser })); }, []); - + const updateStateElement = (elementName, text) => { if (elementName === 'patient') { setState(prevState => ({ ...prevState, patient: text })); @@ -78,47 +82,57 @@ const TaskTab = props => { })); } }; - + const updateStateMap = (elementName, key, text) => { setState(prevState => ({ ...prevState, [elementName]: { ...prevState[elementName], [key]: text } })); }; - + const clearState = () => { setState(prevState => ({ ...prevState, response: {} })); }; - + const handleChange = () => (event, isExpanded) => { setState(prevState => ({ ...prevState, expanded: isExpanded ? true : false })); }; - + return ( - - + + } aria-controls="panel1a-content" id="panel1a-header" > - - + {state.patient?.name ? ( // Display the first name -

{state.patient?.name?.[0]?.given?.[0] + ' ' + state.patient?.name?.[0]?.family}

+ +

+ {state.patient?.name?.[0]?.given?.[0] + ' ' + state.patient?.name?.[0]?.family} +

+
) : ( -

All Patients

+ +

All Patients

+
)}
@@ -147,18 +161,17 @@ const TaskTab = props => { )}
-
- - getPatients()} size="large"> - - - -
- - +
+ + getPatients()} size="large"> + + + +
+
); -} +}; export default TaskTab; diff --git a/src/containers/BackOffice/styles.jsx b/src/containers/BackOffice/styles.jsx index 558b9866..7cbd6d79 100644 --- a/src/containers/BackOffice/styles.jsx +++ b/src/containers/BackOffice/styles.jsx @@ -1,35 +1,33 @@ -import {styled} from '@mui/system'; -import { AppBar, Stack } from '@mui/material' +import { styled } from '@mui/system'; +import { AppBar, Stack } from '@mui/material'; import { makeStyles } from '@mui/styles'; -export default makeStyles( - theme => ({ - loginIcon: { - color: theme.palette.common.white, - fontSize: '19px', - marginLeft: 'auto', - fontFamily: 'Verdana', - float: 'right', - marginRight: '20px', - verticalAlign: 'middle' - }, - whiteButton: { - color: 'white !important', - borderColor: 'white !important', - marginRight: '5px !important', - marginLeft: '20px !important', - }, - patientButton: { - padding: '10px', - 'padding-left': '20px', - 'padding-right': '20px' - } - }) -); - +export default makeStyles(theme => ({ + loginIcon: { + color: theme.palette.common.white, + fontSize: '19px', + marginLeft: 'auto', + fontFamily: 'Verdana', + float: 'right', + marginRight: '20px', + verticalAlign: 'middle' + }, + whiteButton: { + color: 'white !important', + borderColor: 'white !important', + marginRight: '5px !important', + marginLeft: '20px !important' + }, + patientButton: { + padding: '10px', + 'padding-left': '20px', + 'padding-right': '20px' + } +})); -export const StyledStack = styled(Stack)(({ theme, selected, disabled, isscrolled, highlight }) => ({ - position: 'relative', +export const StyledStack = styled(Stack)( + ({ theme, selected, disabled, isscrolled, highlight }) => ({ + position: 'relative', // width: '200px', margin: '0 5px', padding: '8px 20px 8px 20px', @@ -37,28 +35,33 @@ export const StyledStack = styled(Stack)(({ theme, selected, disabled, isscrolle borderRadius: '8px', cursor: disabled ? 'default' : 'pointer', color: disabled ? theme.palette.text.gray : theme.palette.text.primary, - backgroundColor: highlight ? theme.palette.background.primary : (selected && !isscrolled) ? theme.palette.common.offWhite : 'inherit', - transition: `border 0.5s ease`, + backgroundColor: highlight + ? theme.palette.background.primary + : selected && !isscrolled + ? theme.palette.common.offWhite + : 'inherit', + transition: 'border 0.5s ease', border: '1px solid transparent', - borderBottomColor: (selected && isscrolled) ? theme.palette.primary.main : 'transparent', - boxShadow: (selected && !isscrolled) ? 'rgba(0,0,0,0.2) 8px -2px 12px 2px' : 'none', + borderBottomColor: selected && isscrolled ? theme.palette.primary.main : 'transparent', + boxShadow: selected && !isscrolled ? 'rgba(0,0,0,0.2) 8px -2px 12px 2px' : 'none', '&:hover': { - border: disabled ? '' : `1px solid ${theme.palette.common.gray}` + border: disabled ? '' : `1px solid ${theme.palette.common.gray}` } -})); + }) +); export const GlossaryDiv = styled('div')(({ theme, isscrolled }) => ({ - backgroundColor: 'white', - zIndex: 1200, - padding: '30px', + backgroundColor: 'white', + zIndex: 1200, + padding: '30px' })); export const StyledAppBarAlt = styled(AppBar, { - shouldForwardProp: (prop) => prop !== 'open', + shouldForwardProp: prop => prop !== 'open' })(({ theme, open, isscrolled, drawerwidth }) => { - console.log(theme); - console.log(theme.palette); - return { + console.log(theme); + console.log(theme.palette); + return { marginTop: '15px', marginBottom: '15px', marginLeft: '2%', @@ -70,10 +73,10 @@ export const StyledAppBarAlt = styled(AppBar, { color: theme.palette.common.black, boxShadow: isscrolled ? 'rgba(0,0,0,0.2)' : 'none', borderRadius: '8px', - border: isscrolled ? '1px solid #c1c1c1' : 'none', + border: isscrolled ? '1px solid #c1c1c1' : 'none', ...(open && { - marginRight: `calc(2% + ${drawerwidth})`, - width: `calc(96% - ${drawerwidth}px)`, - - }), -}}); \ No newline at end of file + marginRight: `calc(2% + ${drawerwidth})`, + width: `calc(96% - ${drawerwidth}px)` + }) + }; +}); diff --git a/src/containers/ContextProvider/SettingsProvider.jsx b/src/containers/ContextProvider/SettingsProvider.jsx index caaa2be0..3cf836db 100644 --- a/src/containers/ContextProvider/SettingsProvider.jsx +++ b/src/containers/ContextProvider/SettingsProvider.jsx @@ -7,10 +7,9 @@ export const SettingsContext = React.createContext({ dispatch: () => null, updateSetting: () => null, readSettings: () => null, - saveSettings: () => null, + saveSettings: () => null }); - export const SettingsProvider = ({ children }) => { const [state, dispatch] = React.useReducer(reducer, initialState); @@ -44,5 +43,9 @@ export const SettingsProvider = ({ children }) => { localStorage.setItem('reqgenSettings', JSON.stringify(headers)); }; - return {children}; + return ( + + {children} + + ); }; diff --git a/src/containers/Gateway/Gateway.jsx b/src/containers/Gateway/Gateway.jsx index 16f3e09b..4b9f54c9 100644 --- a/src/containers/Gateway/Gateway.jsx +++ b/src/containers/Gateway/Gateway.jsx @@ -31,7 +31,7 @@ const Gateway = props => { FHIR.oauth2.authorize({ clientId: clientId, scope: scope.join(' '), - redirectUri: props.redirect + (backOffice ? "/backoffice": ""), + redirectUri: props.redirect + (backOffice ? '/backoffice' : ''), iss: fhirUrl }); }; @@ -102,7 +102,17 @@ const Gateway = props => { )} /> - {setBackOffice(!backOffice)}} />} label="Back Office" /> + { + setBackOffice(!backOffice); + }} + /> + } + label="Back Office" + /> - + {state.patient?.name ? ( // Display the first name -

{state.patient?.name?.[0]?.given?.[0] + ' ' + state.patient?.name?.[0]?.family}

+ +

+ {state.patient?.name?.[0]?.given?.[0] + ' ' + state.patient?.name?.[0]?.family} +

+
) : ( -

All Patients

+ +

All Patients

+
)} diff --git a/src/util/auth.js b/src/util/auth.js index 5465731c..74e4a998 100644 --- a/src/util/auth.js +++ b/src/util/auth.js @@ -34,9 +34,11 @@ function login() { } function logout() { - window.location.replace(`${env.get('VITE_AUTH').asString()}/realms/${env - .get('VITE_REALM') - .asString()}/protocol/openid-connect/logout`); + window.location.replace( + `${env.get('VITE_AUTH').asString()}/realms/${env + .get('VITE_REALM') + .asString()}/protocol/openid-connect/logout` + ); } /** diff --git a/src/util/buildRequest.js b/src/util/buildRequest.js index 2b67c72f..c0397a07 100644 --- a/src/util/buildRequest.js +++ b/src/util/buildRequest.js @@ -10,7 +10,7 @@ export default function buildRequest( includePrefetch, hook, hookConfig - ) { +) { // Use the provided user if there is no request for this hook let userId = 'Practitioner/' + user; if (request) { diff --git a/src/util/data.js b/src/util/data.js index b0333c9d..a2a506bf 100644 --- a/src/util/data.js +++ b/src/util/data.js @@ -114,24 +114,32 @@ const CDS_SERVICE = 'cds-services'; const ETASU_ENDPOINT = 'GuidanceResponse/$rems-etasu'; const serviceEndpoints = { - 'order-sign': CDS_SERVICE+'/rems-'+ORDER_SIGN, - 'order-select': CDS_SERVICE+'/rems-'+ORDER_SELECT, - 'patient-view': CDS_SERVICE+'/rems-'+PATIENT_VIEW, - 'encounter-start': CDS_SERVICE+'/rems-'+ENCOUNTER_START, - '$rems-etasu': '4_0_0/'+ETASU_ENDPOINT + 'order-sign': CDS_SERVICE + '/rems-' + ORDER_SIGN, + 'order-select': CDS_SERVICE + '/rems-' + ORDER_SELECT, + 'patient-view': CDS_SERVICE + '/rems-' + PATIENT_VIEW, + 'encounter-start': CDS_SERVICE + '/rems-' + ENCOUNTER_START, + '$rems-etasu': '4_0_0/' + ETASU_ENDPOINT }; - const medicationRequestToRemsAdmins = Object.freeze([ { rxnorm: 2183126, display: 'Turalio 200 MG Oral Capsule', endpoints: [ { endpointType: ORDER_SIGN, remsAdmin: 'http://localhost:8090/cds-services/rems-order-sign' }, - { endpointType: ORDER_SELECT, remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' }, - { endpointType: PATIENT_VIEW, remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' }, - { endpointType: ENCOUNTER_START, remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' }, - { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/'+ETASU_ENDPOINT } + { + endpointType: ORDER_SELECT, + remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' + }, + { + endpointType: PATIENT_VIEW, + remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' + }, + { + endpointType: ENCOUNTER_START, + remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' + }, + { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/' + ETASU_ENDPOINT } ] }, { @@ -139,10 +147,19 @@ const medicationRequestToRemsAdmins = Object.freeze([ display: 'Isotretinoin 20 MG Oral Capsule', endpoints: [ { endpointType: ORDER_SIGN, remsAdmin: 'http://localhost:8090/cds-services/rems-order-sign' }, - { endpointType: ORDER_SELECT, remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' }, - { endpointType: PATIENT_VIEW, remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' }, - { endpointType: ENCOUNTER_START, remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' }, - { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/'+ETASU_ENDPOINT } + { + endpointType: ORDER_SELECT, + remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' + }, + { + endpointType: PATIENT_VIEW, + remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' + }, + { + endpointType: ENCOUNTER_START, + remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' + }, + { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/' + ETASU_ENDPOINT } ] }, { @@ -150,10 +167,19 @@ const medicationRequestToRemsAdmins = Object.freeze([ display: 'TIRF 200 UG Oral Transmucosal Lozenge', endpoints: [ { endpointType: ORDER_SIGN, remsAdmin: 'http://localhost:8090/cds-services/rems-order-sign' }, - { endpointType: ORDER_SELECT, remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' }, - { endpointType: PATIENT_VIEW, remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' }, - { endpointType: ENCOUNTER_START, remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' }, - { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/'+ETASU_ENDPOINT } + { + endpointType: ORDER_SELECT, + remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' + }, + { + endpointType: PATIENT_VIEW, + remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' + }, + { + endpointType: ENCOUNTER_START, + remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' + }, + { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/' + ETASU_ENDPOINT } ] }, { @@ -161,10 +187,19 @@ const medicationRequestToRemsAdmins = Object.freeze([ display: 'Addyi 100 MG Oral Tablet', endpoints: [ { endpointType: ORDER_SIGN, remsAdmin: 'http://localhost:8090/cds-services/rems-order-sign' }, - { endpointType: ORDER_SELECT, remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' }, - { endpointType: PATIENT_VIEW, remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' }, - { endpointType: ENCOUNTER_START, remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' }, - { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/'+ETASU_ENDPOINT } + { + endpointType: ORDER_SELECT, + remsAdmin: 'http://localhost:8090/cds-services/rems-order-select' + }, + { + endpointType: PATIENT_VIEW, + remsAdmin: 'http://localhost:8090/cds-services/rems-patient-view' + }, + { + endpointType: ENCOUNTER_START, + remsAdmin: 'http://localhost:8090/cds-services/rems-encounter-start' + }, + { endpointType: REMS_ETASU, remsAdmin: 'http://localhost:8090/4_0_0/' + ETASU_ENDPOINT } ] } ]); diff --git a/src/util/fhir.js b/src/util/fhir.js index 69f751fd..7ad5bd3a 100644 --- a/src/util/fhir.js +++ b/src/util/fhir.js @@ -68,7 +68,7 @@ function createMedicationFromMedicationRequest(medicationRequest) { medication.id = medicationRequest?.id + '-med'; if (medicationRequest.medicationCodeableConcept) { medication.code = medicationRequest.medicationCodeableConcept; - } else if (medicationRequest.medicationReference) { + } else if (medicationRequest.medicationReference) { const reference = medicationRequest?.medicationReference; medication.code = undefined; medicationRequest?.contained?.every(e => { From 8e2c00f6951c8ca2c0da43fa9a09e8b09d0f2797 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 15:11:15 -0400 Subject: [PATCH 7/9] remove pharmacyid from queries --- src/PrefetchTemplate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 1015a266..4772d59a 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -55,7 +55,6 @@ export class PrefetchTemplate { requestBundle, patientReference, userReference, - pharmacyId, ...prefetchKeys ) { var resolvedQueries = new Map(); From b27affda6398856278a6aa68d2c1a3ff138adc0f Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 15:55:13 -0400 Subject: [PATCH 8/9] have the prefetch pharmacy setting work --- src/PrefetchTemplate.js | 28 +++++++++++++++++--------- src/components/SMARTBox/PatientBox.jsx | 26 +++++++++--------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/PrefetchTemplate.js b/src/PrefetchTemplate.js index 4772d59a..30ed1631 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -1,16 +1,19 @@ // Prefetch Template Source: // https://build.fhir.org/ig/HL7/davinci-crd/hooks.html#prefetch export class PrefetchTemplate { - static generatePrefetchMap(includePharmacy = true, pharmacyId = 'pharm0111') { + static generatePrefetchMap(settings = null) { + // If no settings provided, use defaults from data.js + const includePharmacy = settings?.includePharmacyInPreFetch ?? + headerDefinitions.includePharmacyInPreFetch.default; + const pharmacyId = 'pharm0111'; + const prefetchMap = new Map(); const PRACTITIONER_PREFETCH = new PrefetchTemplate('{{context.userId}}'); - const REQUEST_PREFETCH = new PrefetchTemplate( 'MedicationRequest/{{context.medications.MedicationRequest.id}}' ); const PATIENT_PREFETCH = new PrefetchTemplate('{{context.patientId}}'); - const ALL_REQUESTS_PREFETCH = new PrefetchTemplate( 'MedicationRequest?subject={{context.patientId}}&_include=MedicationRequest:medication' ); @@ -21,7 +24,7 @@ export class PrefetchTemplate { prefetchMap.set('patient', PATIENT_PREFETCH); prefetchMap.set('medicationRequests', ALL_REQUESTS_PREFETCH); - // Optional pharmacy prefetch (only if explicitly requested and pharmacyId provided) + // Optional pharmacy prefetch based on settings if (includePharmacy && pharmacyId) { const PHARMACY_PREFETCH = new PrefetchTemplate(`HealthcareService/${pharmacyId}`); prefetchMap.set('pharmacy', PHARMACY_PREFETCH); @@ -55,15 +58,21 @@ export class PrefetchTemplate { requestBundle, patientReference, userReference, + settings = null, ...prefetchKeys ) { + 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]; + if (!prefetchKey || !prefetchMap.has(prefetchKey)) continue; var query = prefetchMap.get(prefetchKey).getQuery(); // 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]; @@ -71,7 +80,8 @@ export class PrefetchTemplate { if (requestBundle) { resolvedParameter = PrefetchTemplate.resolveParameter( unresolvedParameter, - requestBundle + requestBundle, + paramElementMap ); } else { if (unresolvedParameter === 'context.patientId') { @@ -108,8 +118,9 @@ export class PrefetchTemplate { } } - static resolveParameter(unresolvedParameter, requestBundle) { + static resolveParameter(unresolvedParameter, requestBundle, paramElementMap) { const paramField = paramElementMap.get(unresolvedParameter); + if (!paramField) return null; const resolvedParameter = PrefetchTemplate.getProp(requestBundle, paramField); return resolvedParameter; } @@ -123,7 +134,4 @@ export class PrefetchTemplate { getQuery() { return this.query; } -} - -const prefetchMap = PrefetchTemplate.generatePrefetchMap(); -const paramElementMap = PrefetchTemplate.generateParamElementMap(); +} \ No newline at end of file diff --git a/src/components/SMARTBox/PatientBox.jsx b/src/components/SMARTBox/PatientBox.jsx index b4c32b42..a0d2358a 100644 --- a/src/components/SMARTBox/PatientBox.jsx +++ b/src/components/SMARTBox/PatientBox.jsx @@ -1,5 +1,6 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useContext } from 'react'; import { getAge, getDrugCodeFromMedicationRequest } from '../../util/fhir'; +import { SettingsContext } from '../../containers/ContextProvider/SettingsProvider'; import './smart.css'; import { Button } from '@mui/material'; import Tooltip from '@mui/material/Tooltip'; @@ -18,6 +19,8 @@ import { } from '../../util/util'; const PatientBox = props => { + const [globalState] = useContext(SettingsContext); + const [state, setState] = useState({ request: '', deviceRequests: {}, @@ -167,27 +170,16 @@ const PatientBox = props => { const fetchResources = queries => { let requests = []; callback('prefetchCompleted', false); - console.log('🔍 PHARMACY DEBUG - All queries to fetch:', queries); // ADD THIS - queries.forEach((query, queryKey) => { - console.log(`🔍 PHARMACY DEBUG - Processing ${queryKey}: ${query}`); // ADD THIS const urlQuery = '/' + query; requests.push( client .request(urlQuery) .then(response => { - console.log(`🔍 PHARMACY DEBUG - SUCCESS for ${queryKey}:`, response); // ADD THIS return response; }) - .catch(error => { - console.log(`🔍 PHARMACY DEBUG - FAILED for ${queryKey}:`, error); // ADD THIS - return null; // Return null so Promise.all doesn't fail completely - }) .then(resource => { - if (resource) { - console.log(`🔍 PHARMACY DEBUG - Calling callbackMap for ${queryKey}`); // ADD THIS - callbackMap('prefetchedResources', queryKey, resource); - } + callbackMap('prefetchedResources', queryKey, resource); }) ); }); @@ -212,11 +204,11 @@ const PatientBox = props => { request, patientReference, userReference, - 'pharm0111', + globalState, 'request', 'patient', 'practitioner', - 'pharmacy' + globalState.includePharmacyInPreFetch ? 'pharmacy' : undefined ); fetchResources(queries); @@ -230,11 +222,11 @@ const PatientBox = props => { request, patientReference, userReference, - 'pharm0111', + globalState, 'patient', 'practitioner', 'medicationRequests', - 'pharmacy' + globalState.includePharmacyInPreFetch ? 'pharmacy' : undefined ); fetchResources(queries); } From 9e09903e38b92be827c3c1ee452135b8f2928cb3 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Tue, 16 Sep 2025 16:01:53 -0400 Subject: [PATCH 9/9] add settings config for pharmacy id --- .env | 1 + README.md | 3 ++- src/PrefetchTemplate.js | 3 ++- src/util/data.js | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.env b/.env index 6bcb2d09..d4b9e1e8 100644 --- a/.env +++ b/.env @@ -29,3 +29,4 @@ VITE_USE_INTERMEDIARY = false VITE_USE_PHARMACY_IN_PREFETCH = true VITE_INTERMEDIARY = http://localhost:3003 VITE_DISABLE_MEDICATION_STATUS = false +VITE_PHARMACY_ID = pharm0111 diff --git a/README.md b/README.md index 49d36a5c..36084943 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,9 @@ Following are a list of modifiable paths: | VITE_URL | `http://localhost:3000` | The base url of this app. Should be modified if the port or domain change. | | VITE_USER | `alice` | The default user to login as when opening the app. | | 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_INTERMEDIARY | `http://localhost:3030` | The base url of the intermediary. | +| VITE_PHARMACY_ID | `pharm0111` | The pharmacy ID to use in the CDS Hooks Prefetch | # 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 30ed1631..573a8806 100644 --- a/src/PrefetchTemplate.js +++ b/src/PrefetchTemplate.js @@ -5,7 +5,8 @@ export class PrefetchTemplate { // If no settings provided, use defaults from data.js const includePharmacy = settings?.includePharmacyInPreFetch ?? headerDefinitions.includePharmacyInPreFetch.default; - const pharmacyId = 'pharm0111'; + const pharmacyId = settings?.pharmacyId ?? + headerDefinitions.pharmacyId.default; const prefetchMap = new Map(); diff --git a/src/util/data.js b/src/util/data.js index a2a506bf..910383c2 100644 --- a/src/util/data.js +++ b/src/util/data.js @@ -6,6 +6,11 @@ const headerDefinitions = { type: 'check', default: env.get('VITE_USE_PHARMACY_IN_PREFETCH').asBool() }, + pharmacyId: { + display: 'Pharmacy ID', + type: 'input', + default: env.get('VITE_PHARMACY_ID').asString() || 'pharm0111' + }, useIntermediary: { display: 'Use Intermediary', type: 'check',