Skip to content

Commit

Permalink
Merge branch '354-generic-resource-list' of github.com:beda-software/…
Browse files Browse the repository at this point in the history
…fhir-emr into update-app-layout
  • Loading branch information
vesnushka committed Dec 14, 2024
2 parents 026fc2c + 0ecc667 commit 5470fc9
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ auth:
refresh_token: true
secret_required: false
access_token_expiration: 36000
type: smart-on-fhir-practitioner
type: smart-on-fhir-encounter
smart:
name: ADHA CHAP Form
launch_uri: https://portal-xrp.digitalhealth.gov.au/FormsPortal/launch
Expand Down
15 changes: 15 additions & 0 deletions resources/seeds/Client/aus-cvd-risk-i.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
auth:
authorization_code:
redirect_uri: https://main.dlnanw1r5u3dw.amplifyapp.com
refresh_token: true
secret_required: false
access_token_expiration: 36000
type: smart-on-fhir-practitioner
smart:
name: Calculator SMART App
launch_uri: https://main.dlnanw1r5u3dw.amplifyapp.com/launch
description: Calculator SMART App
grant_types:
- authorization_code
id: aus-cvd-risk-i
resourceType: Client
2 changes: 1 addition & 1 deletion src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import { S } from './Modal.styles';
export interface ModalProps extends ANTDModalProps {}

export function Modal(props: ModalProps) {
return <S.Modal {...props} />;
return <S.Modal maskClosable={false} {...props} />;
}
47 changes: 26 additions & 21 deletions src/containers/DocumentsList/ChooseDocumentToCreateModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ interface Props extends ModalProps {
context?: string;
onCancel: () => void;
openNewTab?: boolean;
displayShareButton?: boolean;
}

export const ChooseDocumentToCreateModal = (props: Props) => {
const { subjectType, patient, encounter, onCancel, context, openNewTab } = props;
const { subjectType, patient, encounter, onCancel, context, openNewTab, displayShareButton = true } = props;
const [questionnaireId, setQuestionnaireId] = useState();
const location = useLocation();
const navigate = useNavigate();
Expand Down Expand Up @@ -53,26 +54,30 @@ export const ChooseDocumentToCreateModal = (props: Props) => {
<Button key="back" onClick={onCloseModal}>
<Trans>Cancel</Trans>
</Button>,
<Button
key="share-link"
disabled={!questionnaireId}
onClick={() => {
const questionnaireParams = _.compact([
`patient=${patient.id}`,
`questionnaire=${questionnaireId}`,
encounter?.id ? `encounter=${encounter.id}` : null,
]);
navigator.clipboard.writeText(
`${window.location.origin}/questionnaire?${questionnaireParams.join('&')}`,
);
notification.success({
message: t`The link was copied to clipboard`,
});
onCloseModal();
}}
>
<Trans>Share link</Trans>
</Button>,
...(displayShareButton
? [
<Button
key="share-link"
disabled={!questionnaireId}
onClick={() => {
const questionnaireParams = _.compact([
`patient=${patient.id}`,
`questionnaire=${questionnaireId}`,
encounter?.id ? `encounter=${encounter.id}` : null,
]);
navigator.clipboard.writeText(
`${window.location.origin}/questionnaire?${questionnaireParams.join('&')}`,
);
notification.success({
message: t`The link was copied to clipboard`,
});
onCloseModal();
}}
>
<Trans>Share link</Trans>
</Button>,
]
: []),
<Button
key="create"
disabled={!questionnaireId}
Expand Down
1 change: 1 addition & 0 deletions src/containers/EncounterDetails/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface EncounterDetailsProps {
hideControls?: boolean;
documentTypes?: Array<DocumentType>;
openNewTab?: boolean;
displayShareButton?: boolean;
}

export function useEncounterDetails(props: EncounterDetailsProps) {
Expand Down
15 changes: 13 additions & 2 deletions src/containers/EncounterDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AudioOutlined, CheckOutlined, PlusOutlined } from '@ant-design/icons';
import { t, Trans } from '@lingui/macro';
import { Button, notification } from 'antd';
import { Encounter } from 'fhir/r4b';
import { Encounter, Patient } from 'fhir/r4b';
import { useCallback, useState } from 'react';
import { Link } from 'react-router-dom';

Expand All @@ -20,12 +20,22 @@ import { questionnaireIdLoader } from 'src/hooks/questionnaire-response-form-dat
import { AIScribe, useAIScribe } from './AIScribe';
import { S } from './EncounterDetails.styles';
import { EncounterDetailsProps, useEncounterDetails } from './hooks';
import { PatientApps } from '../PatientDetails/PatientApps';

interface OpenModalState {
open: boolean;
context?: string;
}

interface EncounterAppsProps {
patient: Patient;
encounter: Encounter;
}

function EncounterApps({ patient, encounter }: EncounterAppsProps) {
return <PatientApps patient={patient} encounter={encounter} />;
}

export const EncounterDetails = (props: EncounterDetailsProps) => {
const { patient, hideControls } = props;
const [modalOpened, setModalOpened] = useState<OpenModalState>({ open: false });
Expand Down Expand Up @@ -132,6 +142,7 @@ export const EncounterDetails = (props: EncounterDetailsProps) => {
encounter={encounter}
context={modalOpened.context}
openNewTab={props.openNewTab}
displayShareButton={props.displayShareButton}
/>

{showScriber ||
Expand All @@ -144,7 +155,7 @@ export const EncounterDetails = (props: EncounterDetailsProps) => {
recorderControls={recorderControls}
/>
) : null}

<EncounterApps patient={patient} encounter={encounter} />
<DocumentsList key={documentListKey} patient={patient} />
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe.each(Object.values(Role))('useSmartApps for role %s', (role) => {
[Role.Practitioner]: () => 'smart-on-fhir-practitioner',
[Role.Receptionist]: () => 'smart-on-fhir-practitioner',
});
const firstClient = appData.Client[0];
const firstClient = appData[0];
if (!firstClient) {
throw new Error('First client data is not available');
}
Expand Down
28 changes: 21 additions & 7 deletions src/containers/PatientDetails/PatientApps/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { notification } from 'antd';
import { Encounter } from 'fhir/r4b';

import { useService } from 'aidbox-react/lib/hooks/service';
import { isSuccess } from 'aidbox-react/lib/libs/remoteData';
import { getFHIRResources as getAidboxResources, extractBundleResources } from 'aidbox-react/lib/services/fhir';
import { isSuccess, success } from 'aidbox-react/lib/libs/remoteData';
import { getFHIRResources as getAidboxResources, extractBundleResources, WithId } from 'aidbox-react/lib/services/fhir';
import { mapSuccess, service } from 'aidbox-react/lib/services/service';

import { Client } from '@beda.software/aidbox-types';
import config from '@beda.software/emr-config';

import { matchCurrentUserRole, Role } from 'src/utils/role';

export function useSmartApps() {
export function useSmartApps(encounter?: Encounter) {
const [appsRemoteData] = useService(async () => {
const clientType = matchCurrentUserRole<string>({
let clientType = matchCurrentUserRole<string>({
[Role.Patient]: () => 'smart-on-fhir-patient',
[Role.Admin]: () => 'smart-on-fhir',
[Role.Practitioner]: () => 'smart-on-fhir-practitioner',
[Role.Receptionist]: () => 'smart-on-fhir-practitioner',
});
if (encounter) {
if (clientType === 'smart-on-fhir-practitioner') {
clientType = 'smart-on-fhir-encounter';
} else {
const mockResonse: Array<WithId<Client>> = [];
return success(mockResonse);
}
}
return mapSuccess(
await getAidboxResources<Client>('Client', { ['.type']: clientType }),
extractBundleResources,
(b) => extractBundleResources(b).Client,
);
});
return { appsRemoteData };
Expand All @@ -31,6 +40,7 @@ export interface LaunchProps {
client: string;
patient: string;
practitioner?: string;
encounter?: string;
}

interface LaunchRPCResult {
Expand All @@ -39,7 +49,7 @@ interface LaunchRPCResult {
};
}

export async function launch({ user, client, patient, practitioner }: LaunchProps) {
export async function launch({ user, client, patient, practitioner, encounter }: LaunchProps) {
const response = await service<LaunchRPCResult>({
url: '/rpc',
method: 'POST',
Expand All @@ -49,7 +59,11 @@ export async function launch({ user, client, patient, practitioner }: LaunchProp
user,
iss: encodeURIComponent(`${config.baseURL}/fhir`),
client,
ctx: { patient, ...(practitioner ? { practitioner } : {}) },
ctx: {
patient,
...(practitioner ? { practitioner } : {}),
...(encounter ? { encounter } : {}),
},
},
},
});
Expand Down
15 changes: 9 additions & 6 deletions src/containers/PatientDetails/PatientApps/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Typography, Card, Button } from 'antd';
import { Patient } from 'fhir/r4b';
import { Encounter, Patient } from 'fhir/r4b';

import { RenderRemoteData } from 'aidbox-react/lib/components/RenderRemoteData';

Expand All @@ -13,19 +13,22 @@ const { Text } = Typography;

interface PatientAppsProps {
patient: Patient;
encounter?: Encounter;
}
interface SmartAppProps {
patient: Patient;
app: Client;
encounter?: Encounter;
}

export function useLaunchApp({ app, patient }: SmartAppProps) {
export function useLaunchApp({ app, patient, encounter }: SmartAppProps) {
const currentUser = selectCurrentUserRoleResource();
const launchApp = () => {
const launchParams: LaunchProps = {
client: app.id!,
user: currentUser.id,
patient: patient.id!,
encounter: encounter?.id,
};
if (currentUser.resourceType === 'Practitioner') {
launchParams.practitioner = currentUser.id;
Expand All @@ -52,19 +55,19 @@ function SmartApp(props: SmartAppProps) {
);
}

export function PatientApps({ patient }: PatientAppsProps) {
const { appsRemoteData } = useSmartApps();
export function PatientApps({ patient, encounter }: PatientAppsProps) {
const { appsRemoteData } = useSmartApps(encounter);
return (
<RenderRemoteData remoteData={appsRemoteData}>
{(data) => {
const apps = data.Client ?? [];
const apps = data ?? [];
if (apps.length == 0) {
return <Text>There are no registered smart apps</Text>;
} else {
return (
<>
{apps.map((app) => (
<SmartApp key={app.id} app={app} patient={patient} />
<SmartApp key={app.id} app={app} patient={patient} encounter={encounter} />
))}
</>
);
Expand Down
2 changes: 2 additions & 0 deletions src/containers/PatientDetails/PatientDocuments/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface Props {
title?: string;
context?: string;
openNewTab?: boolean;
displayShareButton?: boolean;
}

export const PatientDocuments = (props: Props) => {
Expand All @@ -35,6 +36,7 @@ export const PatientDocuments = (props: Props) => {
subjectType="Patient"
context={props.context}
openNewTab={props.openNewTab}
displayShareButton={props.displayShareButton}
/>
</div>
)}
Expand Down
25 changes: 20 additions & 5 deletions src/containers/PatientDetails/PatientResources/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,25 @@ import {
Observation,
Patient,
Provenance,
ObservationComponent,
} from 'fhir/r4b';
import { extractExtension } from 'sdc-qrf';

import { WithId } from '@beda.software/fhir-react';

import { ResourceTable, Option, LinkToEdit } from 'src/components/ResourceTable';
import { compileAsArray } from 'src/utils';
import { formatHumanDate, formatHumanDateTime } from 'src/utils/date';

const getInterpretation = compileAsArray<Observation, string>('Observation.interpretation.coding.display');

function getComponentValue(c: ObservationComponent) {
if (c.dataAbsentReason) {
return [c.dataAbsentReason.text ?? 'unknown'];
}
return [`${c.valueQuantity?.value} ${c.valueQuantity?.unit}`];
}

export function getOptions(patient: WithId<Patient>): Option[] {
return [
{
Expand Down Expand Up @@ -236,7 +247,7 @@ export function getOptions(patient: WithId<Patient>): Option[] {
key: 'title',
render: (resource: Observation) => (
<LinkToEdit
name={resource.code?.coding?.[0]?.display}
name={resource.code?.text ?? resource.code?.coding?.[0]?.display}
resource={resource}
provenanceList={provenanceList}
/>
Expand All @@ -259,15 +270,18 @@ export function getOptions(patient: WithId<Patient>): Option[] {
key: 'value',
render: (resource: Observation) => {
if (resource.valueQuantity) {
return `${resource.valueQuantity.value} ${resource.valueQuantity.unit}`;
const interpretation = getInterpretation(resource).join(', ');
return `${resource.valueQuantity.value} ${
resource.valueQuantity.unit ?? ''
} ${interpretation}`;
} else if (resource.component) {
return (
<>
{resource.component
.map((c) =>
[
...[c.code.coding?.[0]?.display],
...[`${c.valueQuantity?.value} ${c.valueQuantity?.unit}`],
...[c.code.text ?? c.code.coding?.[0]?.display],
...getComponentValue(c),
].join(': '),
)
.map((v) => (
Expand All @@ -279,8 +293,9 @@ export function getOptions(patient: WithId<Patient>): Option[] {
return (
resource.valueCodeableConcept.text || resource.valueCodeableConcept.coding?.[0]?.display
);
} else if (resource.interpretation) {
return getInterpretation(resource).join(', ');
}

return null;
},
},
Expand Down

0 comments on commit 5470fc9

Please sign in to comment.