-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(feat) O3-4071: Improve Start Visit Form to support "Visit Locations" #2103
base: main
Are you sure you want to change the base?
Changes from all commits
3436590
da64f87
2634e52
91f07de
520fd95
5c8d57a
422b817
f0b40f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,7 @@ export const esmPatientChartSchema = { | |
}, | ||
disableChangingVisitLocation: { | ||
_type: Type.Boolean, | ||
_description: | ||
"Whether the visit location field in the Start Visit form should be view-only. If so, the visit location will always be set to the user's login location.", | ||
_description: 'Whether the visit location field in the Start Visit form should be view-only.', | ||
_default: false, | ||
}, | ||
disableEmptyTabs: { | ||
|
@@ -61,6 +60,12 @@ export const esmPatientChartSchema = { | |
_description: 'The UUID of the visit type to be used for the automatically created offline visits.', | ||
_default: 'a22733fa-3501-4020-a520-da024eeff088', | ||
}, | ||
restrictByVisitLocationTag: { | ||
_type: Type.Boolean, | ||
_description: | ||
'On the start visit form, whether to restrict the visit location to locations with the Visit Location tag', | ||
_default: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an argument to be made that this should default to 'true', though assumedly this would potentially break some existing implementations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should mention that this requires the EMR API to be installed on the backend? (And maybe even provide a warning if it's set to true without it installed) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And yes, good point, we should definitely note that, will add. |
||
}, | ||
showAllEncountersTab: { | ||
_type: Type.Boolean, | ||
_description: 'Shows the All Encounters Tab of Patient Visits section in Patient Chart', | ||
|
@@ -147,6 +152,7 @@ export interface ChartConfig { | |
notesConceptUuids: string[]; | ||
numberOfVisitsToLoad: number; | ||
offlineVisitTypeUuid: string; | ||
restrictByVisitLocationTag: boolean; | ||
showAllEncountersTab: boolean; | ||
showExtraVisitAttributesSlot: boolean; | ||
showRecommendedVisitTypeTab: boolean; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import { type FetchResponse, openmrsFetch, useConfig } from '@openmrs/esm-framew | |
import useSWRImmutable from 'swr/immutable'; | ||
import { type ChartConfig } from '../../config-schema'; | ||
|
||
export const useDefaultLoginLocation = () => { | ||
export const useDefaultFacilityLocation = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed this to a more accurate name (IMO) |
||
const config = useConfig() as ChartConfig; | ||
const apiUrl = config.defaultFacilityUrl; | ||
const { data, error, isLoading } = useSWRImmutable<FetchResponse>(apiUrl, openmrsFetch); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { type FetchResponse, type Location, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; | ||
import { useEffect } from 'react'; | ||
import useSWR from 'swr'; | ||
|
||
/** | ||
* Fetches the default visit location based on the location and the restrictByVisitLocationTag | ||
* If restrictByVisitLocationTag is true, it fetches the nearest ancestor location that supports visits, otherwise it returns the passed-in location | ||
* | ||
* @param location | ||
* @param restrictByVisitLocationTag | ||
*/ | ||
export function useDefaultVisitLocation(location: Location, restrictByVisitLocationTag: boolean) { | ||
let url = `${restBaseUrl}/emrapi/locationThatSupportsVisits?location=${location?.uuid}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to check the feature flag for whether emrapi is installed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point... I was going this would be "buyer beware" and the implementer would need to know that the EMR API module needed to be installed, but no reason not to add this check as well. |
||
|
||
const { data, error } = useSWR<FetchResponse>(restrictByVisitLocationTag ? url : null, openmrsFetch); | ||
|
||
useEffect(() => { | ||
if (error) { | ||
console.error(error); | ||
} | ||
}, [error]); | ||
|
||
return !restrictByVisitLocationTag ? location : data?.data; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { type FetchResponse, openmrsFetch, type OpenmrsResource, restBaseUrl } from '@openmrs/esm-framework'; | ||
import useSWRImmutable from 'swr/immutable'; | ||
import { useMemo } from 'react'; | ||
|
||
interface EmrApiConfigurationResponse { | ||
atFacilityVisitType: OpenmrsResource; | ||
// TODO: extract this into Core and include all fields (see Ward app) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This hook now existing in two places, here and in the Ward app... I can ticket adding this hook to Core. |
||
} | ||
|
||
const customRepProps = [['atFacilityVisitType', 'ref']]; | ||
|
||
const customRep = `custom:${customRepProps.map((prop) => prop.join(':')).join(',')}`; | ||
|
||
export function useEmrConfiguration(isEmrApiModuleInstalled: boolean) { | ||
const url = isEmrApiModuleInstalled ? `${restBaseUrl}/emrapi/configuration?v=${customRep}` : null; | ||
|
||
const swrData = useSWRImmutable<FetchResponse<EmrApiConfigurationResponse>>(url, openmrsFetch); | ||
|
||
const results = useMemo( | ||
() => ({ | ||
emrConfiguration: swrData.data?.data, | ||
isLoadingEmrConfiguration: swrData.isLoading, | ||
mutateEmrConfiguration: swrData.mutate, | ||
errorFetchingEmrConfiguration: swrData.error, | ||
}), | ||
[swrData], | ||
); | ||
return results; | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,56 @@ | ||
import React, { useState } from 'react'; | ||
import React, { useEffect, useState } from 'react'; | ||
import classNames from 'classnames'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import { ComboBox } from '@carbon/react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { type Control, Controller } from 'react-hook-form'; | ||
import { type Location, type OpenmrsResource, useConfig, useSession } from '@openmrs/esm-framework'; | ||
import { | ||
type Location, | ||
type OpenmrsResource, | ||
useConfig, | ||
useSession, | ||
useLocations, | ||
useFeatureFlag, | ||
} from '@openmrs/esm-framework'; | ||
import { type VisitFormData } from './visit-form.resource'; | ||
import { useDefaultLoginLocation } from '../hooks/useDefaultLocation'; | ||
import { useLocations } from '../hooks/useLocations'; | ||
import { useDefaultFacilityLocation } from '../hooks/useDefaultFacilityLocation'; | ||
import { type ChartConfig } from '../../config-schema'; | ||
import styles from './visit-form.scss'; | ||
import { useDefaultVisitLocation } from '../hooks/useDefaultVisitLocation'; | ||
|
||
interface LocationSelectorProps { | ||
control: Control<VisitFormData>; | ||
} | ||
|
||
const LocationSelector: React.FC<LocationSelectorProps> = ({ control }) => { | ||
const { t } = useTranslation(); | ||
const session = useSession(); | ||
const [searchTerm, setSearchTerm] = useState(''); | ||
const selectedSessionLocation = useSession().sessionLocation; | ||
const { locations } = useLocations(searchTerm); | ||
const { defaultFacility, isLoading: loadingDefaultFacility } = useDefaultLoginLocation(); | ||
const config = useConfig<ChartConfig>(); | ||
const [searchTerm, setSearchTerm] = useState(''); | ||
const sessionLocation = useSession().sessionLocation; | ||
const isEmrApiModuleInstalled = useFeatureFlag('emrapi-module'); | ||
const defaultVisitLocation = useDefaultVisitLocation( | ||
sessionLocation, | ||
config.restrictByVisitLocationTag && isEmrApiModuleInstalled, | ||
); | ||
const locations = useLocations( | ||
config.restrictByVisitLocationTag && isEmrApiModuleInstalled ? 'Visit Location' : null, | ||
searchTerm, | ||
); | ||
const { defaultFacility, isLoading: loadingDefaultFacility } = useDefaultFacilityLocation(); | ||
const disableChangingVisitLocation = config?.disableChangingVisitLocation; | ||
const locationsToShow: Array<OpenmrsResource> = | ||
!loadingDefaultFacility && !isEmpty(defaultFacility) | ||
? [defaultFacility] | ||
: locations | ||
? locations | ||
: selectedSessionLocation | ||
? [selectedSessionLocation] | ||
: []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was making sure that if there were no valid locations, the selected session location still appears in the list... which seemed incorrect. |
||
!loadingDefaultFacility && !isEmpty(defaultFacility) ? [defaultFacility] : locations ? locations : []; | ||
|
||
const handleSearch = (searchString) => { | ||
setSearchTerm(searchString); | ||
}; | ||
|
||
useEffect(() => { | ||
if (config.restrictByVisitLocationTag && !isEmrApiModuleInstalled) { | ||
console.warn('EMR API module is not installed. Visit location will not be restricted by location tag.'); | ||
} | ||
}, [config.restrictByVisitLocationTag, isEmrApiModuleInstalled]); | ||
|
||
return ( | ||
<section data-testid="combo"> | ||
<div className={styles.sectionTitle}>{t('visitLocation', 'Visit Location')}</div> | ||
|
@@ -62,7 +76,7 @@ const LocationSelector: React.FC<LocationSelectorProps> = ({ control }) => { | |
)} | ||
/> | ||
) : ( | ||
<p className={styles.bodyShort02}>{session?.sessionLocation?.display}</p> | ||
<p className={styles.bodyShort02}>{defaultVisitLocation?.display}</p> | ||
)} | ||
</div> | ||
</section> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second half of this statement is no longer true, so I just removed it.