Skip to content

Commit

Permalink
Merge pull request #65 from chronic-care/develop
Browse files Browse the repository at this point in the history
Updates for 2.2.0
  • Loading branch information
swmuir authored May 9, 2024
2 parents 0bb3878 + f3f903b commit 3a67bc5
Show file tree
Hide file tree
Showing 28 changed files with 10,773 additions and 7,362 deletions.
138 changes: 115 additions & 23 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react';
import { Switch, Route, RouteComponentProps } from 'react-router-dom';
import { Tab, Box, Paper } from '@mui/material';
import { TabList, TabPanel, TabContext } from '@mui/lab';
import { Task } from './data-services/fhir-types/fhir-r4';
import { Patient, Task } from './data-services/fhir-types/fhir-r4';

import HomeIcon from '@mui/icons-material/Home';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
Expand Down Expand Up @@ -51,6 +51,8 @@ import ConditionEditForm from './components/edit-forms/ConditionEditForm';
import GoalEditForm from './components/edit-forms/GoalEditForm';
import ProviderLogin from "./components/shared-data/ProviderLogin";
import ShareData from "./components/shared-data/ShareData";
import UnShareData from "./components/unshared-data/UnShareData";

import SharedDataSummary from "./components/shared-data/SharedDataSummary";
import SessionProtected from './components/session-timeout/SessionProtected';
import { SessionTimeoutPage } from './components/session-timeout/SessionTimeoutPage';
Expand Down Expand Up @@ -327,37 +329,78 @@ class App extends React.Component<AppProps, AppState> {
}

setLoadAndMergeSDSIfAvailable = async (launcherPatientId: string | undefined, launcherData: FHIRData) => {

if (launcherPatientId) {
console.log('connect to SDS so we can verify it can exist')
const tempSDSClient = await this.setSupplementalDataClient(launcherPatientId)
const launcherOnlyMessage = "Loading the launcher only, the SDS will not be loaded."

if (this.state.supplementalDataClient && this.state.canShareData) {
// TODO: convert this to use multi login code to see if that resolves issue with duplicate meld original data?
// TODO: convert this to use multi login code?

// Configure the SDS client to get FHIR Data
console.log('We can connect to the SDS, so, add it to loader (read) SDS data')
const serverUrl = this.state.supplementalDataClient.state.serverUrl
const serverUrlFromEnvVar = process.env.REACT_APP_SHARED_DATA_ENDPOINT
console.log(`Dynamic SDS serverUrl (using this for now...): ${serverUrl}`)
console.log(`Static SDS serverUrl (verify it's the same...): ${serverUrlFromEnvVar}`)
console.log('tempSDSClient', tempSDSClient)
console.log('this.state.supplementalDataClient: Is this the same as tempSDS? It should be! If not, then we are sending the wrong data to getFhirData',
console.log(`this.state.supplementalDataClient: Is this the same as tempSDS? It should be!
If not, then we are sending the wrong data to getFhirData`,
this.state.supplementalDataClient)

this.setFhirDataStates(undefined)
this.resetErrorMessageState()
const sdsData: FHIRData = await getFHIRData(true, serverUrl, this.state.supplementalDataClient,
this.setAndLogProgressState, this.setResourcesLoadedCountState, this.setAndLogErrorMessageState)
console.log('SDS data: ', sdsData)
sdsData.serverName = 'SDS Data'
const mergedFhirDataCollection: FHIRData[] = [sdsData, launcherData]
console.log('Merged (launcher and SDS) data', mergedFhirDataCollection)
this.setFhirDataStates(mergedFhirDataCollection)

try {
// Use the SDS client to get FHIR Data
const sdsData: FHIRData = await getFHIRData(true, serverUrl, this.state.supplementalDataClient,
this.setAndLogProgressState, this.setResourcesLoadedCountState, this.setAndLogErrorMessageState)
console.log('SDS data: ', sdsData)
sdsData.serverName = 'SDS Data'

// Merge launcher and SDS Data and set states
const mergedFhirDataCollection: FHIRData[] = [sdsData, launcherData]
console.log('Merged (launcher and SDS) data', mergedFhirDataCollection)
this.setFhirDataStates(mergedFhirDataCollection)
} catch (err) {
// Note: This should be a very rare event
// TODO: Exnternalize this and other exceptions into one function to reduce duplicate code...
const userMessage: string = `There is an issue loading a seemingly valid SDS.
Loading the launcher only.`
const devMessage: string = `The SDS cannot be used due to an error while running
getFHIRData with the SDS client: ` + launcherOnlyMessage
this.setAndLogErrorMessageState('Non-terminating', userMessage, devMessage, err)

// Ensure the app doesn't try to use this invalid client
this.setState({ supplementalDataClient: undefined })
this.setState({ canShareData: false })
// TODO: What other issues might this cause... leftover localForage in getFhirData, etc.?
}

} else {
console.log('No SDS due to !this.state.supplementalDataClient || !this.state.canShareData, so just loading the launcher')
// TODO: Exnternalize this and other exceptions (like the one above)
// into one function to reduce duplicate code...
// TODO: Consider throwing an exception and handling there when else condition met vs using else itself
const userMessage: string = "The SDS is invalid. Loading the launcher only."
const devMessage: string = `The SDS cannot be used due to an invalid SDS configuration,
a missing patient, or otherwise: ` + launcherOnlyMessage
this.setAndLogErrorMessageState('Non-terminating', userMessage, devMessage,
"!this.state.supplementalDataClient || !this.state.canShareData")

// Ensure the app doesn't try to use this invalid client
this.setState({ supplementalDataClient: undefined })
this.setState({ canShareData: false })
// TODO: What other issues might this cause... leftover localForage in getFhirData, etc.?

this.setFhirDataStates([launcherData])
}

} else {
// TODO: Set this to non-terminating as well and include all code from other 'exceptions'?
console.log('No SDS due to !launcherPatientId, so just loading the launcher')
this.setFhirDataStates([launcherData])
}

}

// TODO: MULTI-PROVIDER: This code is copioed into this class for now from the function in ProviderLOgin
Expand Down Expand Up @@ -425,7 +468,8 @@ class App extends React.Component<AppProps, AppState> {
let fhirDataFromStoredEndpoint: FHIRData | undefined = undefined

console.log("fhirDataFromStoredEndpoint = await getFHIRData(true, issServerUrl!)")
// !FUNCTION DIFF!: Props changed to this for setAndLogProgressState, setResourcesLoadedCountState, and setAndLogErrorMessageState,
// !FUNCTION DIFF!: Props changed to this for setAndLogProgressState, setResourcesLoadedCountState,
// and setAndLogErrorMessageState
// TODO SDS: Maybe check if this is the sds, if it is, do the correct getFHIRData call
if (selectedEndpoint.name.includes('SDS') && this.state.supplementalDataClient) {
console.log('loading sds data in App.tsx but not on first load/with a launcher')
Expand Down Expand Up @@ -560,16 +604,57 @@ class App extends React.Component<AppProps, AppState> {

setSupplementalDataClient = async (patientId: string): Promise<Client | undefined> => {
console.log('setSupplementalDataClient()')
const client = await getSupplementalDataClient(patientId)
// const client = await getSupplementalDataClient()
let client = await getSupplementalDataClient(patientId)

if (client) {
const stillValid = await isSavedTokenStillValid(client.state)
this.setState({ supplementalDataClient: client })
this.setState({ canShareData: stillValid })
// We have a valid client for the SDS, but, we don't know if it has any data yet
// (or a valid patient / patient with data)
// Ensure we have data by running a query such as the following before proceeding
// Query to run: https://gw.interop.community/MCCSDSEmpty/open/Patient/petient-id
// If we don't get: "resourceType": "Patient", (and instead get something like: "resourceType": "OperationOutcome")
// Return undefined.
// Note: We are only checking for a patient (below) at this time, can consider adding data check/above query later.
// If we want to go further, and we get back a Patient, we can check that: "id": "patient-name",
// If either of those fail, we don't load the SDS...

const sdsMessageSuffix = "The SDS client will not be used."
let isSDSReadError = false
let sdsPatient: Patient | undefined
if (client.patient.id !== null) {
console.log("client.patient.id !== null, using client.patient.read()")
try {
sdsPatient = await client.patient.read() as Patient
console.log("Valid ")
} catch (err) {
console.warn("Warning: SDS Patient cannot be read via client.patient.read(): " + sdsMessageSuffix)
isSDSReadError = true
}
} else {
console.log("client.patient.id === null, using client.user.read() isntead of client.patient.read()")
try {
sdsPatient = await client.user.read() as Patient
} catch (err) {
console.warn("Warning: SDS Patient cannot be read via client.user.read(): " + sdsMessageSuffix)
isSDSReadError = true
}
}

if (!isSDSReadError) {
console.log("Valid SDS patient read: Using SDS client", sdsPatient ? sdsPatient : "unknown")

const stillValid = await isSavedTokenStillValid(client.state)
this.setState({ supplementalDataClient: client })
this.setState({ canShareData: stillValid })

console.log("***** PatientID = " + client.getPatientId() ?? "")
console.log("***** User ID = " + client.getUserId() ?? "")
console.log("***** Can share data = " + stillValid ?? "?")
} else {
console.warn(`Warning: Invalid SDS patient read: Overriding valid client to undefined
and not setting state for supplementalDataClient or canShareData`)
client = undefined
}

console.log("***** PatientID = " + client.getPatientId() ?? "")
console.log("***** User ID = " + client.getUserId() ?? "")
console.log("***** Can share data = " + stillValid ?? "?")
}
return client
}
Expand Down Expand Up @@ -698,7 +783,7 @@ class App extends React.Component<AppProps, AppState> {
public render(): JSX.Element {
// process.env.REACT_APP_DEBUG_LOG === "true" && console.log("APP component RENDERED!")

let patient = this.state.patientSummaries;
// let patient = this.state.patientSummaries;
let editFormData: EditFormData = {
fhirDataCollection: this.state.fhirDataCollection,
patientSummaries: this.state.patientSummaries,
Expand Down Expand Up @@ -758,6 +843,11 @@ class App extends React.Component<AppProps, AppState> {
<ShareData fhirDataCollection={this.state.fhirDataCollection} />
</SessionProtected>
</Route>
<Route path="/unshare-data">
<SessionProtected isLoggedIn={!this.state.isLogout}>
<UnShareData fhirDataCollection={this.state.fhirDataCollection} />
</SessionProtected>
</Route>
<Route path="/shared-data-summary">
<SessionProtected isLoggedIn={!this.state.isLogout}>
<SharedDataSummary />
Expand Down Expand Up @@ -802,7 +892,9 @@ class App extends React.Component<AppProps, AppState> {
<TabPanel value="1" sx={{ padding: '0px 15px 100px' }}>
<Home fhirDataCollection={this.state.fhirDataCollection} patientSummaries={this.state.patientSummaries} screenings={this.state.screenings}
progressMessage={this.state.progressMessage} progressValue={this.state.progressValue} resourcesLoadedCount={this.state.resourcesLoadedCount}
errorType={this.state.errorType} userErrorMessage={this.state.userErrorMessage} developerErrorMessage={this.state.developerErrorMessage} errorCaught={this.state.errorCaught} />
errorType={this.state.errorType} userErrorMessage={this.state.userErrorMessage} developerErrorMessage={this.state.developerErrorMessage} errorCaught={this.state.errorCaught}
canShareData={this.state.canShareData}
/>
</TabPanel>
<TabPanel value="2" sx={{ padding: '0px 0px 100px' }}>
<TabContext value={this.state.planTabIndex}>
Expand Down
42 changes: 27 additions & 15 deletions src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface HomeProps {
userErrorMessage: string | undefined,
developerErrorMessage: string | undefined,
errorCaught: Error | string | unknown,
canShareData?: boolean
}

interface HomeState {
Expand Down Expand Up @@ -52,15 +53,15 @@ export default class Home extends React.Component<HomeProps, HomeState> {
// TODO:MULTI-PROVIDER: Change patient name list to provider name and display single patient name at top
public render(): JSX.Element {

const sdsurl = process.env.REACT_APP_SHARED_DATA_ENDPOINT;
const sdsurl = process.env.REACT_APP_SHARED_DATA_ENDPOINT


let fhirDataCollection = this.props.fhirDataCollection
let patients = this.props.patientSummaries;
let screenings = this.props.screenings?.filter(s => s.notifyPatient);
let patients = this.props.patientSummaries
let screenings = this.props.screenings?.filter(s => s.notifyPatient)
// let tasks = this.props.tasks;

const hhsBanner = process.env.REACT_APP_HHS_BANNER === 'false';
const hhsBanner = process.env.REACT_APP_HHS_BANNER === 'false'

return (
<div className="home-view">
Expand All @@ -76,7 +77,7 @@ export default class Home extends React.Component<HomeProps, HomeState> {
{!fhirDataCollection || (fhirDataCollection && (fhirDataCollection[0]?.caregiverName === undefined)) ? '' :
<p className="subheadline">Caregiver <b>{fhirDataCollection && fhirDataCollection[0]?.caregiverName}</b></p>
}
{(patients === undefined) ? '' :
{(!patients) ? '' :
<div className="subheadline">
{!fhirDataCollection || (fhirDataCollection && (fhirDataCollection[0]?.caregiverName === undefined)) ? '' : 'for '}
{/* <b>{patient?.fullName}</b> ({patient?.gender}) Age {patient?.age} */}
Expand All @@ -94,9 +95,10 @@ export default class Home extends React.Component<HomeProps, HomeState> {
return (
<li key={index}>
{
fhirDataCollection && fhirDataCollection[index].isSDS ?
fhirDataCollection && fhirDataCollection[index] && fhirDataCollection[index]?.isSDS ?
<><b>SDS for {curPatient?.fullName}</b> (age {curPatient?.age}) {fhirDataCollection[index].serverName} </> :
<><b>{curPatient?.fullName}</b> (age {curPatient?.age}) {fhirDataCollection![index].serverName} </>
(fhirDataCollection && fhirDataCollection[index]) &&
<><b>{curPatient?.fullName}</b> (age {curPatient?.age}) {fhirDataCollection[index].serverName} </>
// TODO: Consider adding an isLauncher option (need to add to datatype first)
}
</li>
Expand Down Expand Up @@ -177,26 +179,36 @@ export default class Home extends React.Component<HomeProps, HomeState> {
</ul>
}



<div>

<p>
<h5 style={{ paddingTop: '20px' }}>Shared Health Records</h5>
<Link to={{ pathname: '/provider-login', state: { fhirDataCollection: this.props.fhirDataCollection } }}>Retrieve records from additional healthcare providers</Link>
<h5 style={{ paddingTop: '20px' }}>Add a health record account</h5>
<Link to={{ pathname: '/provider-login', state: { fhirDataCollection: this.props.fhirDataCollection } }}>Login to additional healthcare provider accounts</Link>
</p>

</div>


<div>
{typeof sdsurl !== 'undefined' ? (
{ this.props.canShareData ? (
<p>
<h5 style={{ paddingTop: '20px' }}>Share your health data</h5>
<Link to={{ pathname: '/share-data' }}>Share your health data</Link></p>
) : (<p></p>)}
</div>


<div>
{ this.props.canShareData ? (
<p>
<h5 style={{ paddingTop: '20px' }}>Withdraw your health data</h5>
<Link to={{ pathname: '/unshare-data' }}>Opt out of sharing your health data</Link></p>
) : (<p></p>)}
</div>


<h5 style={{ paddingTop: '20px' }}>Disclaimer</h5>
<p>This application is provided for informational purposes only and does not constitute medical advice or professional services. The information provided should not be used for diagnosing or treating a health problem or disease, and those seeking personal medical advice should consult with a licensed physician. Always seek the advice of your doctor or other qualified health provider regarding a medical condition. Never disregard professional medical advice or delay in seeking it because of something you have read in this application. If you think you may have a medical emergency, call 911 or go to the nearest emergency room immediately. No physician-patient relationship is created by this application or its use. Neither OHSU, nor its employees, nor any contributor to this application, makes any representations, express or implied, with respect to the information herein or to its use.</p>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/session-timeout/SessionTimeoutHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const SessionTimeOutHandler = (props: SessionTimeOutHandlerProps) => {
showModal={showModal}
handleContinue={handleContinueSession}
handleLogout={handleLogout}
isLoggedOut={isLogout}
/>
</div>
)
Expand Down
Loading

0 comments on commit 3a67bc5

Please sign in to comment.