@medplum/react 1.0.6 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/OperationOutcomeAlert/OperationOutcomeAlert.d.ts +6 -0
- package/dist/cjs/ReferenceRangeEditor/ReferenceRangeEditor.d.ts +1 -2
- package/dist/cjs/ResourceTimeline/ResourceTimeline.d.ts +2 -2
- package/dist/cjs/auth/AuthenticationForm.d.ts +15 -1
- package/dist/cjs/auth/SignInForm.d.ts +1 -0
- package/dist/cjs/index.cjs +150 -167
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/esm/DefaultResourceTimeline/DefaultResourceTimeline.mjs +6 -18
- package/dist/esm/DefaultResourceTimeline/DefaultResourceTimeline.mjs.map +1 -1
- package/dist/esm/EncounterTimeline/EncounterTimeline.mjs +7 -24
- package/dist/esm/EncounterTimeline/EncounterTimeline.mjs.map +1 -1
- package/dist/esm/OperationOutcomeAlert/OperationOutcomeAlert.d.ts +6 -0
- package/dist/esm/OperationOutcomeAlert/OperationOutcomeAlert.mjs +13 -0
- package/dist/esm/OperationOutcomeAlert/OperationOutcomeAlert.mjs.map +1 -0
- package/dist/esm/PatientTimeline/PatientTimeline.mjs +13 -20
- package/dist/esm/PatientTimeline/PatientTimeline.mjs.map +1 -1
- package/dist/esm/ReferenceRangeEditor/ReferenceRangeEditor.d.ts +1 -2
- package/dist/esm/ReferenceRangeEditor/ReferenceRangeEditor.mjs.map +1 -1
- package/dist/esm/ResourcePropertyDisplay/ResourcePropertyDisplay.mjs +2 -2
- package/dist/esm/ResourcePropertyDisplay/ResourcePropertyDisplay.mjs.map +1 -1
- package/dist/esm/ResourcePropertyInput/ResourcePropertyInput.mjs +2 -2
- package/dist/esm/ResourcePropertyInput/ResourcePropertyInput.mjs.map +1 -1
- package/dist/esm/ResourceTimeline/ResourceTimeline.d.ts +2 -2
- package/dist/esm/ResourceTimeline/ResourceTimeline.mjs +19 -24
- package/dist/esm/ResourceTimeline/ResourceTimeline.mjs.map +1 -1
- package/dist/esm/ServiceRequestTimeline/ServiceRequestTimeline.mjs +8 -30
- package/dist/esm/ServiceRequestTimeline/ServiceRequestTimeline.mjs.map +1 -1
- package/dist/esm/auth/AuthenticationForm.d.ts +15 -1
- package/dist/esm/auth/AuthenticationForm.mjs +66 -34
- package/dist/esm/auth/AuthenticationForm.mjs.map +1 -1
- package/dist/esm/auth/NewUserForm.mjs +3 -4
- package/dist/esm/auth/NewUserForm.mjs.map +1 -1
- package/dist/esm/auth/SignInForm.d.ts +1 -0
- package/dist/esm/auth/SignInForm.mjs +27 -19
- package/dist/esm/auth/SignInForm.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/package.json +17 -17
- package/rollup.config.mjs +0 -110
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { OperationOutcomeIssue } from '@medplum/fhirtypes';
|
|
3
|
+
export interface OperationOutcomeAlertProps {
|
|
4
|
+
issues?: OperationOutcomeIssue[];
|
|
5
|
+
}
|
|
6
|
+
export declare function OperationOutcomeAlert(props: OperationOutcomeAlertProps): JSX.Element | null;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { ObservationDefinition, ObservationDefinitionQualifiedInterval } from '@medplum/fhirtypes';
|
|
3
|
-
declare const intervalFilters: readonly ["gender", "age", "gestationalAge", "context", "appliesTo"];
|
|
4
3
|
export interface ReferenceRangeEditorProps {
|
|
5
4
|
definition: ObservationDefinition;
|
|
6
5
|
onSubmit: (result: ObservationDefinition) => void;
|
|
7
6
|
}
|
|
8
7
|
type IntervalGroup = {
|
|
9
8
|
id: string;
|
|
10
|
-
filters: Record<
|
|
9
|
+
filters: Record<string, any>;
|
|
11
10
|
intervals: ObservationDefinitionQualifiedInterval[];
|
|
12
11
|
};
|
|
13
12
|
export declare function ReferenceRangeEditor(props: ReferenceRangeEditorProps): JSX.Element;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { ProfileResource } from '@medplum/core';
|
|
2
|
+
import { MedplumClient, ProfileResource } from '@medplum/core';
|
|
3
3
|
import { Attachment, Bundle, Communication, Media, Reference, Resource } from '@medplum/fhirtypes';
|
|
4
4
|
export interface ResourceTimelineProps<T extends Resource> {
|
|
5
5
|
value: T | Reference<T>;
|
|
6
|
-
|
|
6
|
+
loadTimelineResources: (medplum: MedplumClient, resource: T) => Promise<Bundle[]>;
|
|
7
7
|
createCommunication?: (resource: T, sender: ProfileResource, text: string) => Communication;
|
|
8
8
|
createMedia?: (resource: T, operator: ProfileResource, attachment: Attachment) => Media;
|
|
9
9
|
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { BaseLoginRequest, LoginAuthenticationResponse } from '@medplum/core';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
export interface AuthenticationFormProps extends BaseLoginRequest {
|
|
4
|
-
readonly generatePkce?: boolean;
|
|
5
4
|
readonly onForgotPassword?: () => void;
|
|
6
5
|
readonly onRegister?: () => void;
|
|
7
6
|
readonly handleAuthResponse: (response: LoginAuthenticationResponse) => void;
|
|
8
7
|
readonly children?: React.ReactNode;
|
|
9
8
|
}
|
|
10
9
|
export declare function AuthenticationForm(props: AuthenticationFormProps): JSX.Element;
|
|
10
|
+
export interface EmailFormProps extends BaseLoginRequest {
|
|
11
|
+
readonly generatePkce?: boolean;
|
|
12
|
+
readonly onRegister?: () => void;
|
|
13
|
+
readonly handleAuthResponse: (response: LoginAuthenticationResponse) => void;
|
|
14
|
+
readonly setEmail: (email: string) => void;
|
|
15
|
+
readonly children?: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export declare function EmailForm(props: EmailFormProps): JSX.Element;
|
|
18
|
+
export interface PasswordFormProps extends BaseLoginRequest {
|
|
19
|
+
readonly email: string;
|
|
20
|
+
readonly onForgotPassword?: () => void;
|
|
21
|
+
readonly handleAuthResponse: (response: LoginAuthenticationResponse) => void;
|
|
22
|
+
readonly children?: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export declare function PasswordForm(props: PasswordFormProps): JSX.Element;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseLoginRequest } from '@medplum/core';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
export interface SignInFormProps extends BaseLoginRequest {
|
|
4
|
+
readonly login?: string;
|
|
4
5
|
readonly chooseScopes?: boolean;
|
|
5
6
|
readonly onSuccess?: () => void;
|
|
6
7
|
readonly onForgotPassword?: () => void;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -630,6 +630,13 @@
|
|
|
630
630
|
return undefined;
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
+
function OperationOutcomeAlert(props) {
|
|
634
|
+
if (!props.issues) {
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
return (React.createElement(core$1.Alert, { icon: React.createElement(icons.IconAlertCircle, { size: 16 }), color: "red" }, props.issues.map((issue) => (React.createElement("div", { "data-testid": "text-field-error", key: issue.details?.text }, issue.details?.text)))));
|
|
638
|
+
}
|
|
639
|
+
|
|
633
640
|
/**
|
|
634
641
|
* Dynamically loads the recaptcha script.
|
|
635
642
|
* We do not want to load the script on page load unless the user needs it.
|
|
@@ -684,12 +691,11 @@
|
|
|
684
691
|
}
|
|
685
692
|
} },
|
|
686
693
|
React.createElement(core$1.Center, { sx: { flexDirection: 'column' } }, props.children),
|
|
687
|
-
|
|
694
|
+
React.createElement(OperationOutcomeAlert, { issues: issues }),
|
|
688
695
|
googleClientId && (React.createElement(React.Fragment, null,
|
|
689
696
|
React.createElement(core$1.Group, { position: "center", p: "xl", style: { height: 70 } },
|
|
690
697
|
React.createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: async (response) => {
|
|
691
698
|
try {
|
|
692
|
-
await medplum.startPkce();
|
|
693
699
|
props.handleAuthResponse(await medplum.startGoogleLogin({
|
|
694
700
|
googleClientId: response.clientId,
|
|
695
701
|
googleCredential: response.credential,
|
|
@@ -757,47 +763,79 @@
|
|
|
757
763
|
}
|
|
758
764
|
|
|
759
765
|
function AuthenticationForm(props) {
|
|
760
|
-
const
|
|
766
|
+
const [email, setEmail] = React.useState();
|
|
767
|
+
if (!email) {
|
|
768
|
+
return React.createElement(EmailForm, { setEmail: setEmail, ...props });
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
return React.createElement(PasswordForm, { email: email, ...props });
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function EmailForm(props) {
|
|
775
|
+
const { setEmail, onRegister, handleAuthResponse, children, ...baseLoginRequest } = props;
|
|
761
776
|
const medplum = useMedplum();
|
|
762
777
|
const googleClientId = getGoogleClientId(props.googleClientId);
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
if (generatePkce) {
|
|
767
|
-
await medplum.startPkce();
|
|
778
|
+
const isExternalAuth = React.useCallback(async (authMethod) => {
|
|
779
|
+
if (!authMethod.authorizeUrl) {
|
|
780
|
+
return false;
|
|
768
781
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
782
|
+
const state = JSON.stringify({
|
|
783
|
+
...(await medplum.ensureCodeChallenge(baseLoginRequest)),
|
|
784
|
+
domain: authMethod.domain,
|
|
785
|
+
});
|
|
786
|
+
const url = new URL(authMethod.authorizeUrl);
|
|
787
|
+
url.searchParams.set('state', state);
|
|
788
|
+
window.location.assign(url.toString());
|
|
789
|
+
return true;
|
|
790
|
+
}, [medplum, baseLoginRequest]);
|
|
791
|
+
const handleSubmit = React.useCallback(async (formData) => {
|
|
792
|
+
const authMethod = await medplum.post('auth/method', { email: formData.email });
|
|
793
|
+
if (!(await isExternalAuth(authMethod))) {
|
|
794
|
+
setEmail(formData.email);
|
|
795
|
+
}
|
|
796
|
+
}, [medplum, isExternalAuth, setEmail]);
|
|
797
|
+
const handleGoogleCredential = React.useCallback(async (response) => {
|
|
798
|
+
const authResponse = await medplum.startGoogleLogin({
|
|
799
|
+
...baseLoginRequest,
|
|
800
|
+
googleCredential: response.credential,
|
|
801
|
+
});
|
|
802
|
+
if (!(await isExternalAuth(authResponse))) {
|
|
803
|
+
handleAuthResponse(authResponse);
|
|
804
|
+
}
|
|
805
|
+
}, [medplum, baseLoginRequest, isExternalAuth, handleAuthResponse]);
|
|
806
|
+
return (React.createElement(Form, { style: { maxWidth: 400 }, onSubmit: handleSubmit },
|
|
781
807
|
React.createElement(core$1.Center, { sx: { flexDirection: 'column' } }, children),
|
|
782
|
-
issues && (React.createElement(core$1.Alert, { icon: React.createElement(icons.IconAlertCircle, { size: 16 }), color: "red" }, issues.map((issue) => (React.createElement("div", { "data-testid": "text-field-error", key: issue.details?.text }, issue.details?.text))))),
|
|
783
808
|
googleClientId && (React.createElement(React.Fragment, null,
|
|
784
809
|
React.createElement(core$1.Group, { position: "center", p: "xl", style: { height: 70 } },
|
|
785
|
-
React.createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential:
|
|
786
|
-
startPkce()
|
|
787
|
-
.then(() => medplum.startGoogleLogin({
|
|
788
|
-
...baseLoginRequest,
|
|
789
|
-
googleCredential: response.credential,
|
|
790
|
-
}))
|
|
791
|
-
.then(props.handleAuthResponse)
|
|
792
|
-
.catch(setOutcome);
|
|
793
|
-
} })),
|
|
810
|
+
React.createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: handleGoogleCredential })),
|
|
794
811
|
React.createElement(core$1.Divider, { label: "or", labelPosition: "center", my: "lg" }))),
|
|
812
|
+
React.createElement(core$1.TextInput, { name: "email", type: "email", label: "Email", placeholder: "name@domain.com", required: true, autoFocus: true }),
|
|
813
|
+
React.createElement(core$1.Group, { position: "apart", mt: "xl", spacing: 0, noWrap: true },
|
|
814
|
+
onRegister && (React.createElement(core$1.Anchor, { component: "button", type: "button", color: "dimmed", onClick: onRegister, size: "xs" }, "Register")),
|
|
815
|
+
React.createElement(core$1.Button, { type: "submit" }, "Next"))));
|
|
816
|
+
}
|
|
817
|
+
function PasswordForm(props) {
|
|
818
|
+
const { onForgotPassword, handleAuthResponse, children, ...baseLoginRequest } = props;
|
|
819
|
+
const medplum = useMedplum();
|
|
820
|
+
const [outcome, setOutcome] = React.useState();
|
|
821
|
+
const issues = getIssuesForExpression(outcome, undefined);
|
|
822
|
+
const handleSubmit = React.useCallback((formData) => {
|
|
823
|
+
medplum
|
|
824
|
+
.startLogin({
|
|
825
|
+
...baseLoginRequest,
|
|
826
|
+
password: formData.password,
|
|
827
|
+
remember: formData.remember === 'on',
|
|
828
|
+
})
|
|
829
|
+
.then(handleAuthResponse)
|
|
830
|
+
.catch(setOutcome);
|
|
831
|
+
}, [medplum, baseLoginRequest, handleAuthResponse]);
|
|
832
|
+
return (React.createElement(Form, { style: { maxWidth: 400 }, onSubmit: handleSubmit },
|
|
833
|
+
React.createElement(core$1.Center, { sx: { flexDirection: 'column' } }, children),
|
|
834
|
+
React.createElement(OperationOutcomeAlert, { issues: issues }),
|
|
795
835
|
React.createElement(core$1.Stack, { spacing: "xl" },
|
|
796
|
-
React.createElement(core$1.TextInput, { name: "email", type: "email", label: "Email", placeholder: "name@domain.com", required: true, autoFocus: true, error: getErrorsForInput(outcome, 'email') }),
|
|
797
836
|
React.createElement(core$1.PasswordInput, { name: "password", type: "password", label: "Password", autoComplete: "off", required: true, error: getErrorsForInput(outcome, 'password') })),
|
|
798
837
|
React.createElement(core$1.Group, { position: "apart", mt: "xl", spacing: 0, noWrap: true },
|
|
799
838
|
onForgotPassword && (React.createElement(core$1.Anchor, { component: "button", type: "button", color: "dimmed", onClick: onForgotPassword, size: "xs" }, "Forgot password")),
|
|
800
|
-
onRegister && (React.createElement(core$1.Anchor, { component: "button", type: "button", color: "dimmed", onClick: onRegister, size: "xs" }, "Register")),
|
|
801
839
|
React.createElement(core$1.Checkbox, { id: "remember", name: "remember", label: "Remember me", size: "xs", sx: { lineHeight: 1 } }),
|
|
802
840
|
React.createElement(core$1.Button, { type: "submit" }, "Sign in"))));
|
|
803
841
|
}
|
|
@@ -884,7 +922,22 @@
|
|
|
884
922
|
const [login, setLogin] = React.useState(undefined);
|
|
885
923
|
const [mfaRequired, setAuthenticatorRequired] = React.useState(false);
|
|
886
924
|
const [memberships, setMemberships] = React.useState(undefined);
|
|
887
|
-
|
|
925
|
+
const handleCode = React.useCallback((code) => {
|
|
926
|
+
if (onCode) {
|
|
927
|
+
onCode(code);
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
medplum
|
|
931
|
+
.processCode(code)
|
|
932
|
+
.then(() => {
|
|
933
|
+
if (onSuccess) {
|
|
934
|
+
onSuccess();
|
|
935
|
+
}
|
|
936
|
+
})
|
|
937
|
+
.catch(console.log);
|
|
938
|
+
}
|
|
939
|
+
}, [medplum, onCode, onSuccess]);
|
|
940
|
+
const handleAuthResponse = React.useCallback((response) => {
|
|
888
941
|
setAuthenticatorRequired(!!response.mfaRequired);
|
|
889
942
|
if (response.login) {
|
|
890
943
|
setLogin(response.login);
|
|
@@ -900,28 +953,21 @@
|
|
|
900
953
|
handleCode(response.code);
|
|
901
954
|
}
|
|
902
955
|
}
|
|
903
|
-
}
|
|
904
|
-
|
|
956
|
+
}, [chooseScopes, handleCode]);
|
|
957
|
+
const handleScopeResponse = React.useCallback((response) => {
|
|
905
958
|
handleCode(response.code);
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
if (
|
|
909
|
-
onCode(code);
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
959
|
+
}, [handleCode]);
|
|
960
|
+
React.useEffect(() => {
|
|
961
|
+
if (props.login) {
|
|
912
962
|
medplum
|
|
913
|
-
.
|
|
914
|
-
.then(
|
|
915
|
-
|
|
916
|
-
onSuccess();
|
|
917
|
-
}
|
|
918
|
-
})
|
|
919
|
-
.catch(console.log);
|
|
963
|
+
.get('auth/login/' + props.login)
|
|
964
|
+
.then(handleAuthResponse)
|
|
965
|
+
.catch(console.error);
|
|
920
966
|
}
|
|
921
|
-
}
|
|
967
|
+
}, [medplum, props, handleAuthResponse]);
|
|
922
968
|
return (React.createElement(Document, { width: 450 }, (() => {
|
|
923
969
|
if (!login) {
|
|
924
|
-
return (React.createElement(AuthenticationForm, {
|
|
970
|
+
return (React.createElement(AuthenticationForm, { onForgotPassword: onForgotPassword, onRegister: onRegister, handleAuthResponse: handleAuthResponse, ...baseLoginRequest }, props.children));
|
|
925
971
|
}
|
|
926
972
|
else if (mfaRequired) {
|
|
927
973
|
return React.createElement(MfaForm, { login: login, handleAuthResponse: handleAuthResponse });
|
|
@@ -1198,7 +1244,7 @@
|
|
|
1198
1244
|
if (!property?.path) {
|
|
1199
1245
|
throw Error(`Displaying property of type ${props.propertyType} requires element definition path`);
|
|
1200
1246
|
}
|
|
1201
|
-
return (React.createElement(BackboneElementDisplay, { value: { type: core.
|
|
1247
|
+
return (React.createElement(BackboneElementDisplay, { value: { type: core.getElementDefinitionTypeName(property), value }, compact: true, ignoreMissingValues: props.ignoreMissingValues }));
|
|
1202
1248
|
}
|
|
1203
1249
|
}
|
|
1204
1250
|
/**
|
|
@@ -2195,7 +2241,7 @@
|
|
|
2195
2241
|
case core.PropertyType.UsageContext:
|
|
2196
2242
|
return (React.createElement(BackboneElementInput, { typeName: propertyType, defaultValue: value, onChange: props.onChange, outcome: props.outcome }));
|
|
2197
2243
|
default:
|
|
2198
|
-
return (React.createElement(BackboneElementInput, { typeName: core.
|
|
2244
|
+
return (React.createElement(BackboneElementInput, { typeName: core.getElementDefinitionTypeName(property), defaultValue: value, onChange: props.onChange, outcome: props.outcome }));
|
|
2199
2245
|
}
|
|
2200
2246
|
}
|
|
2201
2247
|
function getTargetTypes(property) {
|
|
@@ -2753,7 +2799,7 @@
|
|
|
2753
2799
|
const resource = useResource(props.value);
|
|
2754
2800
|
const [history, setHistory] = React.useState();
|
|
2755
2801
|
const [items, setItems] = React.useState([]);
|
|
2756
|
-
const
|
|
2802
|
+
const loadTimelineResources = props.loadTimelineResources;
|
|
2757
2803
|
const itemsRef = React.useRef(items);
|
|
2758
2804
|
itemsRef.current = items;
|
|
2759
2805
|
const loadTimeline = React.useCallback(() => {
|
|
@@ -2762,8 +2808,8 @@
|
|
|
2762
2808
|
setHistory({});
|
|
2763
2809
|
return;
|
|
2764
2810
|
}
|
|
2765
|
-
medplum
|
|
2766
|
-
}, [medplum, resource,
|
|
2811
|
+
loadTimelineResources(medplum, resource).then(handleBatchResponse).catch(console.log);
|
|
2812
|
+
}, [medplum, resource, loadTimelineResources]);
|
|
2767
2813
|
React.useEffect(() => {
|
|
2768
2814
|
loadTimeline();
|
|
2769
2815
|
}, [loadTimeline]);
|
|
@@ -2771,29 +2817,24 @@
|
|
|
2771
2817
|
* Handles a batch request response.
|
|
2772
2818
|
* @param batchResponse The batch response.
|
|
2773
2819
|
*/
|
|
2774
|
-
function handleBatchResponse(
|
|
2820
|
+
function handleBatchResponse(bundles) {
|
|
2775
2821
|
const newItems = [];
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
for (const entry of bundle.entry) {
|
|
2788
|
-
if (entry.resource) {
|
|
2789
|
-
newItems.push(entry.resource);
|
|
2790
|
-
}
|
|
2791
|
-
}
|
|
2822
|
+
for (const bundle of bundles) {
|
|
2823
|
+
if (!bundle) {
|
|
2824
|
+
// User may not have access to all resource types
|
|
2825
|
+
continue;
|
|
2826
|
+
}
|
|
2827
|
+
if (bundle.type === 'history') {
|
|
2828
|
+
setHistory(bundle);
|
|
2829
|
+
}
|
|
2830
|
+
if (bundle.entry) {
|
|
2831
|
+
for (const entry of bundle.entry) {
|
|
2832
|
+
newItems.push(entry.resource);
|
|
2792
2833
|
}
|
|
2793
2834
|
}
|
|
2794
|
-
sortByDateAndPriority(newItems);
|
|
2795
|
-
newItems.reverse();
|
|
2796
2835
|
}
|
|
2836
|
+
sortByDateAndPriority(newItems);
|
|
2837
|
+
newItems.reverse();
|
|
2797
2838
|
setItems(newItems);
|
|
2798
2839
|
}
|
|
2799
2840
|
/**
|
|
@@ -2914,10 +2955,10 @@
|
|
|
2914
2955
|
React.createElement(AttachmentButton, { onUpload: createMedia, onUploadStart: onUploadStart, onUploadProgress: onUploadProgress }, (props) => (React.createElement(core$1.ActionIcon, { ...props, radius: "xl", color: "blue", variant: "filled" },
|
|
2915
2956
|
React.createElement(icons.IconCloudUpload, { size: 16 })))))))),
|
|
2916
2957
|
items.map((item) => {
|
|
2958
|
+
const key = `${item.resourceType}/${item.id}/${item.meta?.versionId}`;
|
|
2917
2959
|
if (item.resourceType === resource.resourceType && item.id === resource.id) {
|
|
2918
|
-
return (React.createElement(HistoryTimelineItem, { key:
|
|
2960
|
+
return (React.createElement(HistoryTimelineItem, { key: key, history: history, resource: item, onDetails: onVersionDetails }));
|
|
2919
2961
|
}
|
|
2920
|
-
const key = `${item.resourceType}/${item.id}`;
|
|
2921
2962
|
switch (item.resourceType) {
|
|
2922
2963
|
case 'AuditEvent':
|
|
2923
2964
|
return React.createElement(AuditEventTimelineItem, { key: key, resource: item, onDetails: onDetails });
|
|
@@ -3006,51 +3047,22 @@
|
|
|
3006
3047
|
}
|
|
3007
3048
|
|
|
3008
3049
|
function DefaultResourceTimeline(props) {
|
|
3009
|
-
return (React.createElement(ResourceTimeline, { value: props.resource,
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
method: 'GET',
|
|
3016
|
-
url: `${core.getReferenceString(resource)}/_history`,
|
|
3017
|
-
},
|
|
3018
|
-
},
|
|
3019
|
-
{
|
|
3020
|
-
request: {
|
|
3021
|
-
method: 'GET',
|
|
3022
|
-
url: `AuditEvent?entity=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
|
|
3023
|
-
},
|
|
3024
|
-
},
|
|
3025
|
-
],
|
|
3026
|
-
}) }));
|
|
3050
|
+
return (React.createElement(ResourceTimeline, { value: props.resource, loadTimelineResources: async (medplum, resource) => {
|
|
3051
|
+
return Promise.all([
|
|
3052
|
+
medplum.readHistory(resource.resourceType, resource.id),
|
|
3053
|
+
medplum.search('AuditEvent', '_sort=-_lastUpdated&entity=' + core.getReferenceString(resource)),
|
|
3054
|
+
]);
|
|
3055
|
+
} }));
|
|
3027
3056
|
}
|
|
3028
3057
|
|
|
3029
3058
|
function EncounterTimeline(props) {
|
|
3030
|
-
return (React.createElement(ResourceTimeline, { value: props.encounter,
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
url: `${core.getReferenceString(resource)}/_history`,
|
|
3038
|
-
},
|
|
3039
|
-
},
|
|
3040
|
-
{
|
|
3041
|
-
request: {
|
|
3042
|
-
method: 'GET',
|
|
3043
|
-
url: `Communication?encounter=${core.getReferenceString(resource)}`,
|
|
3044
|
-
},
|
|
3045
|
-
},
|
|
3046
|
-
{
|
|
3047
|
-
request: {
|
|
3048
|
-
method: 'GET',
|
|
3049
|
-
url: `Media?encounter=${core.getReferenceString(resource)}`,
|
|
3050
|
-
},
|
|
3051
|
-
},
|
|
3052
|
-
],
|
|
3053
|
-
}), createCommunication: (resource, sender, text) => ({
|
|
3059
|
+
return (React.createElement(ResourceTimeline, { value: props.encounter, loadTimelineResources: async (medplum, resource) => {
|
|
3060
|
+
return Promise.all([
|
|
3061
|
+
medplum.readHistory('Encounter', resource.id),
|
|
3062
|
+
medplum.search('Communication', 'encounter=' + core.getReferenceString(resource)),
|
|
3063
|
+
medplum.search('Media', 'encounter=' + core.getReferenceString(resource)),
|
|
3064
|
+
]);
|
|
3065
|
+
}, createCommunication: (resource, sender, text) => ({
|
|
3054
3066
|
resourceType: 'Communication',
|
|
3055
3067
|
status: 'completed',
|
|
3056
3068
|
encounter: core.createReference(resource),
|
|
@@ -4523,26 +4535,19 @@
|
|
|
4523
4535
|
}
|
|
4524
4536
|
const MemoizedFhirPathTable = React.memo(FhirPathTable);
|
|
4525
4537
|
|
|
4526
|
-
const searches = [
|
|
4527
|
-
'$/_history',
|
|
4528
|
-
'Communication?subject=$',
|
|
4529
|
-
'Device?patient=$',
|
|
4530
|
-
'DeviceRequest?patient=$',
|
|
4531
|
-
'DiagnosticReport?subject=$',
|
|
4532
|
-
'Media?subject=$',
|
|
4533
|
-
'ServiceRequest?subject=$',
|
|
4534
|
-
];
|
|
4535
4538
|
function PatientTimeline(props) {
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4539
|
+
const loadTimelineResources = React.useCallback((medplum, resource) => {
|
|
4540
|
+
return Promise.all([
|
|
4541
|
+
medplum.readHistory('Patient', resource.id),
|
|
4542
|
+
medplum.search('Communication', 'subject=' + core.getReferenceString(resource)),
|
|
4543
|
+
medplum.search('Device', 'patient=' + core.getReferenceString(resource)),
|
|
4544
|
+
medplum.search('DeviceRequest', 'patient=' + core.getReferenceString(resource)),
|
|
4545
|
+
medplum.search('DiagnosticReport', 'subject=' + core.getReferenceString(resource)),
|
|
4546
|
+
medplum.search('Media', 'subject=' + core.getReferenceString(resource)),
|
|
4547
|
+
medplum.search('ServiceRequest', 'subject=' + core.getReferenceString(resource)),
|
|
4548
|
+
]);
|
|
4549
|
+
}, []);
|
|
4550
|
+
return (React.createElement(ResourceTimeline, { value: props.patient, loadTimelineResources: loadTimelineResources, createCommunication: (resource, sender, text) => ({
|
|
4546
4551
|
resourceType: 'Communication',
|
|
4547
4552
|
status: 'completed',
|
|
4548
4553
|
subject: core.createReference(resource),
|
|
@@ -6129,36 +6134,14 @@
|
|
|
6129
6134
|
}
|
|
6130
6135
|
|
|
6131
6136
|
function ServiceRequestTimeline(props) {
|
|
6132
|
-
return (React.createElement(ResourceTimeline, { value: props.serviceRequest,
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
},
|
|
6141
|
-
},
|
|
6142
|
-
{
|
|
6143
|
-
request: {
|
|
6144
|
-
method: 'GET',
|
|
6145
|
-
url: `Communication?based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
|
|
6146
|
-
},
|
|
6147
|
-
},
|
|
6148
|
-
{
|
|
6149
|
-
request: {
|
|
6150
|
-
method: 'GET',
|
|
6151
|
-
url: `Media?_count=100&based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
|
|
6152
|
-
},
|
|
6153
|
-
},
|
|
6154
|
-
{
|
|
6155
|
-
request: {
|
|
6156
|
-
method: 'GET',
|
|
6157
|
-
url: `DiagnosticReport?based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
|
|
6158
|
-
},
|
|
6159
|
-
},
|
|
6160
|
-
],
|
|
6161
|
-
}), createCommunication: (resource, sender, text) => ({
|
|
6137
|
+
return (React.createElement(ResourceTimeline, { value: props.serviceRequest, loadTimelineResources: async (medplum, resource) => {
|
|
6138
|
+
return Promise.all([
|
|
6139
|
+
medplum.readHistory('ServiceRequest', resource.id),
|
|
6140
|
+
medplum.search('Communication', 'based-on=' + core.getReferenceString(resource)),
|
|
6141
|
+
medplum.search('Media', '_count=100&based-on=' + core.getReferenceString(resource)),
|
|
6142
|
+
medplum.search('DiagnosticReport', 'based-on=' + core.getReferenceString(resource)),
|
|
6143
|
+
]);
|
|
6144
|
+
}, createCommunication: (resource, sender, text) => ({
|
|
6162
6145
|
resourceType: 'Communication',
|
|
6163
6146
|
status: 'completed',
|
|
6164
6147
|
basedOn: [core.createReference(resource)],
|