@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.1-pre.101
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/README.md +58 -12
- package/dist/153.js +1 -0
- package/dist/153.js.map +1 -0
- package/dist/233.js +2 -0
- package/dist/{382.js.LICENSE.txt → 233.js.LICENSE.txt} +3 -2
- package/dist/233.js.map +1 -0
- package/dist/262.js +1 -0
- package/dist/262.js.map +1 -0
- package/dist/279.js +1 -0
- package/dist/279.js.map +1 -0
- package/dist/294.js +1 -2
- package/dist/294.js.LICENSE.txt +2 -7
- package/dist/294.js.map +1 -1
- package/dist/327.js +1 -0
- package/dist/327.js.map +1 -0
- package/dist/409.js +2 -0
- package/dist/409.js.LICENSE.txt +27 -0
- package/dist/409.js.map +1 -0
- package/dist/415.js +1 -2
- package/dist/415.js.map +1 -1
- package/dist/559.js +1 -0
- package/dist/559.js.map +1 -0
- package/dist/574.js +1 -1
- package/dist/651.js +1 -0
- package/dist/651.js.map +1 -0
- package/dist/706.js +1 -0
- package/dist/706.js.map +1 -0
- package/dist/757.js +1 -0
- package/dist/800.js +2 -0
- package/dist/800.js.LICENSE.txt +5 -0
- package/dist/800.js.map +1 -0
- package/dist/820.js +1 -0
- package/dist/820.js.map +1 -0
- package/dist/883.js +1 -0
- package/dist/883.js.map +1 -0
- package/dist/889.js +1 -0
- package/dist/889.js.map +1 -0
- package/dist/897.js +2 -0
- package/dist/897.js.LICENSE.txt +21 -0
- package/dist/897.js.map +1 -0
- package/dist/92.js +1 -0
- package/dist/92.js.map +1 -0
- package/dist/935.js +2 -0
- package/dist/{735.js.LICENSE.txt → 935.js.LICENSE.txt} +6 -16
- package/dist/935.js.map +1 -0
- package/dist/959.js +1 -0
- package/dist/959.js.map +1 -0
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +374 -89
- package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -1
- package/dist/routes.json +1 -0
- package/docs/config-icrc-forms.png +0 -0
- package/docs/config-other-forms.png +0 -0
- package/docs/configuring-form-categories.md +77 -0
- package/docs/fde-workflow.mov +0 -0
- package/docs/form-workflow-state-diagram.png +0 -0
- package/jest.config.json +21 -18
- package/package.json +100 -106
- package/src/CancelModal.tsx +48 -0
- package/src/CompleteModal.tsx +46 -0
- package/src/FormBootstrap.tsx +166 -0
- package/src/Root.tsx +14 -3
- package/src/add-group-modal/AddGroupModal.tsx +288 -0
- package/src/add-group-modal/styles.scss +45 -0
- package/src/config-schema.ts +85 -31
- package/src/context/FormWorkflowContext.tsx +126 -0
- package/src/context/FormWorkflowReducer.ts +287 -0
- package/src/context/GroupFormWorkflowContext.tsx +176 -0
- package/src/context/GroupFormWorkflowReducer.ts +430 -0
- package/src/empty-state/EmptyDataIllustration.tsx +51 -0
- package/src/empty-state/EmptyState.tsx +33 -0
- package/src/empty-state/styles.scss +55 -0
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +196 -0
- package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +50 -0
- package/src/form-entry-workflow/form-review-card/index.ts +3 -0
- package/src/form-entry-workflow/form-review-card/styles.scss +39 -0
- package/src/form-entry-workflow/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
- package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +86 -0
- package/src/form-entry-workflow/patient-banner/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/styles.scss +45 -0
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +63 -0
- package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
- package/src/form-entry-workflow/patient-search-header/styles.scss +22 -0
- package/src/form-entry-workflow/styles.scss +65 -0
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +35 -0
- package/src/form-entry-workflow/workflow-review/index.ts +3 -0
- package/src/form-entry-workflow/workflow-review/styles.scss +34 -0
- package/src/forms-app-menu-link.tsx +3 -2
- package/src/forms-page/FormsPage.tsx +134 -0
- package/src/forms-page/forms-table/FormsTable.tsx +137 -0
- package/src/forms-page/forms-table/index.ts +3 -0
- package/src/forms-page/forms-table/styles.scss +20 -0
- package/src/forms-page/index.ts +3 -0
- package/src/forms-page/styles.scss +11 -0
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
- package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
- package/src/group-form-entry-workflow/SessionDetailsForm.tsx +131 -0
- package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
- package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +144 -0
- package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.test.tsx +9 -0
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +63 -0
- package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-display-header/styles.scss +60 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +139 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +68 -0
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +150 -0
- package/src/group-form-entry-workflow/group-search/compact-group-result.scss +64 -0
- package/src/group-form-entry-workflow/group-search/compact-group-search.scss +35 -0
- package/src/group-form-entry-workflow/group-search/group-search.scss +96 -0
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +73 -0
- package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
- package/src/group-form-entry-workflow/index.ts +3 -0
- package/src/group-form-entry-workflow/styles.scss +97 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useFormState.ts +23 -0
- package/src/hooks/useGetAllForms.ts +45 -0
- package/src/hooks/useGetEncounter.ts +21 -0
- package/src/hooks/useGetPatient.ts +23 -0
- package/src/hooks/useGetPatients.ts +34 -0
- package/src/hooks/useGetSystemSetting.ts +38 -0
- package/src/hooks/useKeyPress.ts +31 -0
- package/src/hooks/usePostEndpoint.ts +76 -0
- package/src/hooks/useSearchEndpoint.ts +120 -0
- package/src/hooks/useStartVisit.ts +92 -0
- package/src/index.ts +26 -62
- package/src/patient-card/PatientCard.tsx +67 -0
- package/src/patient-card/index.ts +3 -0
- package/src/patient-card/styles.scss +46 -0
- package/src/routes.json +24 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/en.json +69 -4
- package/translations/fr.json +50 -0
- package/tsconfig.json +26 -23
- package/.editorconfig +0 -12
- package/.eslintignore +0 -2
- package/.eslintrc +0 -4
- package/.github/workflows/node.js.yml +0 -79
- package/.husky/pre-commit +0 -6
- package/.husky/pre-push +0 -6
- package/.prettierignore +0 -14
- package/dist/24.js +0 -3
- package/dist/24.js.LICENSE.txt +0 -16
- package/dist/24.js.map +0 -1
- package/dist/296.js +0 -2
- package/dist/296.js.map +0 -1
- package/dist/299.js +0 -2
- package/dist/299.js.map +0 -1
- package/dist/382.js +0 -3
- package/dist/382.js.map +0 -1
- package/dist/595.js +0 -3
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/595.js.map +0 -1
- package/dist/69.js +0 -2
- package/dist/69.js.map +0 -1
- package/dist/735.js +0 -3
- package/dist/735.js.map +0 -1
- package/dist/777.js +0 -2
- package/dist/777.js.map +0 -1
- package/dist/860.js +0 -2
- package/dist/860.js.map +0 -1
- package/dist/906.js +0 -2
- package/dist/906.js.map +0 -1
- package/dist/openmrs-esm-fast-data-entry-app.old +0 -2
- package/src/boxes/extensions/blue-box.tsx +0 -15
- package/src/boxes/extensions/box.scss +0 -23
- package/src/boxes/extensions/brand-box.tsx +0 -15
- package/src/boxes/extensions/red-box.tsx +0 -15
- package/src/boxes/slot/boxes.css +0 -23
- package/src/boxes/slot/boxes.tsx +0 -19
- package/src/forms/FormsRoot.tsx +0 -32
- package/src/forms/FormsTable.tsx +0 -64
- package/src/forms/mockData.ts +0 -43
- package/src/greeter/greeter.css +0 -4
- package/src/greeter/greeter.test.tsx +0 -29
- package/src/greeter/greeter.tsx +0 -25
- package/src/hello.css +0 -3
- package/src/hello.test.tsx +0 -45
- package/src/hello.tsx +0 -30
- package/src/patient-getter/patient-getter.resource.ts +0 -31
- package/src/patient-getter/patient-getter.test.tsx +0 -28
- package/src/patient-getter/patient-getter.tsx +0 -28
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { openmrsFetch } from "@openmrs/esm-framework";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
|
|
4
|
+
const encounterUrl = "/ws/rest/v1/encounter/";
|
|
5
|
+
|
|
6
|
+
const useGetEncounter = (encounterUuid) => {
|
|
7
|
+
const url = `${encounterUrl}${encounterUuid}`;
|
|
8
|
+
const { data, error } = useSWR(url, async () => {
|
|
9
|
+
const res = await openmrsFetch(url);
|
|
10
|
+
const encounter = res.data || null;
|
|
11
|
+
return encounter;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
encounter: data,
|
|
16
|
+
isLoading: !error && data,
|
|
17
|
+
error,
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default useGetEncounter;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fetchCurrentPatient } from "@openmrs/esm-framework";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const useGetPatient = (patientUuid) => {
|
|
5
|
+
const [patient, setPatient] = useState(null);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!patientUuid) {
|
|
9
|
+
setPatient(null);
|
|
10
|
+
} else {
|
|
11
|
+
getPatient(patientUuid);
|
|
12
|
+
}
|
|
13
|
+
}, [patientUuid]);
|
|
14
|
+
|
|
15
|
+
const getPatient = async (uuid) => {
|
|
16
|
+
const result = await fetchCurrentPatient(uuid);
|
|
17
|
+
setPatient(result);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return patient;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useGetPatient;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fetchCurrentPatient } from "@openmrs/esm-framework";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const useGetPatients = (patientUuids) => {
|
|
5
|
+
const [patients, setPatients] = useState([]);
|
|
6
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (!patientUuids || patientUuids.length === 0) {
|
|
10
|
+
setPatients([]);
|
|
11
|
+
setIsLoading(false);
|
|
12
|
+
} else {
|
|
13
|
+
getPatients(patientUuids);
|
|
14
|
+
}
|
|
15
|
+
}, [patientUuids]);
|
|
16
|
+
|
|
17
|
+
const getPatients = async (uuids) => {
|
|
18
|
+
try {
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
const results = await Promise.all(
|
|
21
|
+
uuids.map(async (uuid) => await fetchCurrentPatient(uuid))
|
|
22
|
+
);
|
|
23
|
+
setPatients(results);
|
|
24
|
+
setIsLoading(false);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("Error fetching patients:", error);
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return { patients, isLoading };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default useGetPatients;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { openmrsFetch } from "@openmrs/esm-framework";
|
|
3
|
+
|
|
4
|
+
const useGetSystemSetting = (settingId) => {
|
|
5
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
6
|
+
const [result, setResult] = useState(null);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
|
|
9
|
+
const onResult = useCallback((result) => {
|
|
10
|
+
setIsSubmitting(false);
|
|
11
|
+
setError(false);
|
|
12
|
+
setResult(result);
|
|
13
|
+
}, []);
|
|
14
|
+
|
|
15
|
+
const onError = useCallback((error) => {
|
|
16
|
+
setIsSubmitting(false);
|
|
17
|
+
setResult(null);
|
|
18
|
+
setError(error);
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const getSetting = useCallback(() => {
|
|
22
|
+
openmrsFetch(`/ws/rest/v1/systemsetting?q=${settingId}&v=default`)
|
|
23
|
+
.then(onResult)
|
|
24
|
+
.catch(onError);
|
|
25
|
+
}, [onError, onResult, settingId]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
getSetting();
|
|
29
|
+
}, [getSetting]);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
result,
|
|
33
|
+
error,
|
|
34
|
+
isSubmitting,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default useGetSystemSetting;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
const useKeyPress = (targetKey) => {
|
|
4
|
+
const [keyPressed, setKeyPressed] = useState(false);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const downHandler = ({ key }) => {
|
|
8
|
+
if (key === targetKey) {
|
|
9
|
+
setKeyPressed(true);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const upHandler = ({ key }) => {
|
|
14
|
+
if (key === targetKey) {
|
|
15
|
+
setKeyPressed(false);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
window.addEventListener("keydown", downHandler);
|
|
20
|
+
window.addEventListener("keyup", upHandler);
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener("keydown", downHandler);
|
|
24
|
+
window.removeEventListener("keyup", upHandler);
|
|
25
|
+
};
|
|
26
|
+
}, [targetKey]);
|
|
27
|
+
|
|
28
|
+
return keyPressed;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default useKeyPress;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { openmrsFetch } from "@openmrs/esm-framework";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const usePostEndpoint = ({ endpointUrl }) => {
|
|
5
|
+
const [submissionInProgress, setSubmissionInProgress] = useState(null);
|
|
6
|
+
const [result, setResult] = useState(null);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
|
|
9
|
+
const onFormPosted = useCallback(
|
|
10
|
+
(result) => {
|
|
11
|
+
setSubmissionInProgress(false);
|
|
12
|
+
if (error) {
|
|
13
|
+
setError(null);
|
|
14
|
+
}
|
|
15
|
+
setResult(result.data);
|
|
16
|
+
},
|
|
17
|
+
[error]
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const onError = useCallback(
|
|
21
|
+
(error) => {
|
|
22
|
+
setSubmissionInProgress(false);
|
|
23
|
+
if (result) {
|
|
24
|
+
setResult(null);
|
|
25
|
+
}
|
|
26
|
+
setError(error?.responseBody?.error ?? error?.responseBody ?? error);
|
|
27
|
+
},
|
|
28
|
+
[result]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const post = useCallback(
|
|
32
|
+
async (data) => {
|
|
33
|
+
setSubmissionInProgress(true);
|
|
34
|
+
|
|
35
|
+
let path = endpointUrl;
|
|
36
|
+
if (data.uuid) {
|
|
37
|
+
path += "/" + data.uuid;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return openmrsFetch(path, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
},
|
|
45
|
+
body: data,
|
|
46
|
+
})
|
|
47
|
+
.then(onFormPosted)
|
|
48
|
+
.catch(onError);
|
|
49
|
+
},
|
|
50
|
+
[endpointUrl, onError, onFormPosted]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const reset = () => {
|
|
54
|
+
setSubmissionInProgress(null);
|
|
55
|
+
setResult(null);
|
|
56
|
+
setError(null);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
post,
|
|
61
|
+
isPosting: submissionInProgress,
|
|
62
|
+
result,
|
|
63
|
+
error,
|
|
64
|
+
reset,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const usePostVisit = () => {
|
|
69
|
+
return usePostEndpoint({ endpointUrl: "/ws/rest/v1/visit" });
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const usePostCohort = () => {
|
|
73
|
+
return usePostEndpoint({ endpointUrl: "/ws/rest/v1/cohortm/cohort" });
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { usePostEndpoint, usePostVisit, usePostCohort };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { openmrsFetch, FetchResponse } from "@openmrs/esm-framework";
|
|
2
|
+
import { useCallback, useMemo } from "react";
|
|
3
|
+
import useSWRInfinite from "swr/infinite";
|
|
4
|
+
|
|
5
|
+
export interface SearchResponse {
|
|
6
|
+
data: Array<Record<string, unknown>> | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
error: Error;
|
|
9
|
+
loadingNewData: boolean;
|
|
10
|
+
hasMore: boolean;
|
|
11
|
+
currentPage: number;
|
|
12
|
+
totalResults: number;
|
|
13
|
+
setPage: (size: number | ((_size: number) => number)) => Promise<
|
|
14
|
+
FetchResponse<{
|
|
15
|
+
results: Array<Record<string, unknown>>;
|
|
16
|
+
links: Array<{
|
|
17
|
+
rel: "prev" | "next";
|
|
18
|
+
}>;
|
|
19
|
+
}>[]
|
|
20
|
+
>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SearchInfiniteProps {
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
searchTerm: string;
|
|
26
|
+
parameters?: Record<string, unknown> | undefined;
|
|
27
|
+
searching: boolean;
|
|
28
|
+
resultsToFetch?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const useSearchEndpointInfinite = (
|
|
32
|
+
arg0: SearchInfiniteProps
|
|
33
|
+
): SearchResponse => {
|
|
34
|
+
const {
|
|
35
|
+
baseUrl,
|
|
36
|
+
searchTerm,
|
|
37
|
+
parameters,
|
|
38
|
+
searching = true,
|
|
39
|
+
resultsToFetch = 10,
|
|
40
|
+
} = arg0;
|
|
41
|
+
|
|
42
|
+
const getUrl = useCallback(
|
|
43
|
+
(
|
|
44
|
+
page: number,
|
|
45
|
+
prevPageData: FetchResponse<{
|
|
46
|
+
results: Array<Record<string, unknown>>;
|
|
47
|
+
links: Array<{ rel: "prev" | "next" }>;
|
|
48
|
+
}>
|
|
49
|
+
) => {
|
|
50
|
+
if (
|
|
51
|
+
prevPageData &&
|
|
52
|
+
!prevPageData?.data?.links.some((link) => link.rel === "next")
|
|
53
|
+
) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
let url = `${baseUrl}?q=${searchTerm}`;
|
|
57
|
+
const params = {
|
|
58
|
+
// merge passed parameters and default parameters
|
|
59
|
+
// this way the defaults can be overriden if needed
|
|
60
|
+
totalCount: true,
|
|
61
|
+
limit: resultsToFetch,
|
|
62
|
+
...parameters,
|
|
63
|
+
};
|
|
64
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
65
|
+
// don't send null parmeters
|
|
66
|
+
if (value !== null && value !== undefined) {
|
|
67
|
+
url += `&${key}=${value}`;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (page) {
|
|
71
|
+
url += `&startIndex=${page * resultsToFetch}`;
|
|
72
|
+
}
|
|
73
|
+
return url;
|
|
74
|
+
},
|
|
75
|
+
[baseUrl, searchTerm, parameters, resultsToFetch]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const { data, isValidating, setSize, error, size } = useSWRInfinite<
|
|
79
|
+
FetchResponse<{
|
|
80
|
+
results: Array<Record<string, unknown>>;
|
|
81
|
+
links: Array<{ rel: "prev" | "next" }>;
|
|
82
|
+
totalCount: number;
|
|
83
|
+
}>,
|
|
84
|
+
Error
|
|
85
|
+
>(searching ? getUrl : null, openmrsFetch);
|
|
86
|
+
|
|
87
|
+
const results = useMemo(
|
|
88
|
+
() => ({
|
|
89
|
+
data: data
|
|
90
|
+
? [].concat(...(data?.map((resp) => resp?.data?.results) ?? []))
|
|
91
|
+
: null,
|
|
92
|
+
isLoading: !data && !error,
|
|
93
|
+
error,
|
|
94
|
+
hasMore: data?.length
|
|
95
|
+
? !!data[data.length - 1].data?.links?.some(
|
|
96
|
+
(link) => link.rel === "next"
|
|
97
|
+
)
|
|
98
|
+
: false,
|
|
99
|
+
loadingNewData: isValidating,
|
|
100
|
+
setPage: setSize,
|
|
101
|
+
currentPage: size,
|
|
102
|
+
totalResults: data?.[0]?.data?.totalCount,
|
|
103
|
+
}),
|
|
104
|
+
[data, isValidating, error, setSize, size]
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const useSearchCohortInfinite = ({
|
|
111
|
+
...props
|
|
112
|
+
}: SearchInfiniteProps): SearchResponse => {
|
|
113
|
+
return useSearchEndpointInfinite({
|
|
114
|
+
baseUrl: "/ws/rest/v1/cohortm/cohort",
|
|
115
|
+
resultsToFetch: 10,
|
|
116
|
+
...props,
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export { useSearchEndpointInfinite, useSearchCohortInfinite };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import {
|
|
4
|
+
showNotification,
|
|
5
|
+
showToast,
|
|
6
|
+
openmrsFetch,
|
|
7
|
+
} from "@openmrs/esm-framework";
|
|
8
|
+
|
|
9
|
+
const useStartVisit = ({
|
|
10
|
+
showSuccessNotification = true,
|
|
11
|
+
showErrorNotification = true,
|
|
12
|
+
}) => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
15
|
+
const [success, setSuccess] = useState(null);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
|
|
18
|
+
const onSave = useCallback(
|
|
19
|
+
(result) => {
|
|
20
|
+
setIsSubmitting(false);
|
|
21
|
+
setError(false);
|
|
22
|
+
setSuccess(result);
|
|
23
|
+
if (showSuccessNotification) {
|
|
24
|
+
showToast({
|
|
25
|
+
critical: true,
|
|
26
|
+
kind: "success",
|
|
27
|
+
description: t(
|
|
28
|
+
"visitStartedSuccessfully",
|
|
29
|
+
`${result?.data?.visitType?.display} started successfully`
|
|
30
|
+
),
|
|
31
|
+
title: t("visitStarted", "Visit started"),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[t, showSuccessNotification]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const onError = useCallback(
|
|
39
|
+
(error) => {
|
|
40
|
+
setIsSubmitting(false);
|
|
41
|
+
setSuccess(false);
|
|
42
|
+
setError(error);
|
|
43
|
+
if (showErrorNotification) {
|
|
44
|
+
showNotification({
|
|
45
|
+
title: t("startVisitError", "Error starting visit"),
|
|
46
|
+
kind: "error",
|
|
47
|
+
critical: true,
|
|
48
|
+
description: error?.message,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[t, showErrorNotification]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const saveVisit = useCallback(
|
|
56
|
+
(data) => {
|
|
57
|
+
const payload = {
|
|
58
|
+
patient: data.patientUuid,
|
|
59
|
+
startDatetime: data.startDatetime,
|
|
60
|
+
stopDatetime: data.stopDatetime,
|
|
61
|
+
visitType: data.visitType,
|
|
62
|
+
location: data.location,
|
|
63
|
+
};
|
|
64
|
+
openmrsFetch("/ws/rest/v1/visit", {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body: payload,
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
})
|
|
69
|
+
.then(onSave)
|
|
70
|
+
.catch(onError);
|
|
71
|
+
},
|
|
72
|
+
[onError, onSave]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const updateEncounter = useCallback((data) => {
|
|
76
|
+
openmrsFetch("/ws/rest/v1/encounter/" + data.uuid, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
body: { visit: data.visit },
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
});
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
saveVisit,
|
|
85
|
+
updateEncounter,
|
|
86
|
+
success,
|
|
87
|
+
error,
|
|
88
|
+
isSubmitting,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default useStartVisit;
|
package/src/index.ts
CHANGED
|
@@ -1,75 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { getAsyncLifecycle, defineConfigSchema } from "@openmrs/esm-framework";
|
|
1
|
+
import {
|
|
2
|
+
getAsyncLifecycle,
|
|
3
|
+
defineConfigSchema,
|
|
4
|
+
registerBreadcrumbs,
|
|
5
|
+
} from "@openmrs/esm-framework";
|
|
9
6
|
import { configSchema } from "./config-schema";
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
const moduleName = "@openmrs/esm-fast-data-entry-app";
|
|
9
|
+
|
|
10
|
+
const options = {
|
|
11
|
+
featureName: "fast-data-entry-app",
|
|
12
|
+
moduleName,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const importTranslation = require.context(
|
|
17
16
|
"../translations",
|
|
18
17
|
false,
|
|
19
18
|
/.json$/,
|
|
20
19
|
"lazy"
|
|
21
20
|
);
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
* This tells the app shell what versions of what OpenMRS backend modules
|
|
25
|
-
* are expected. Warnings will appear if suitable modules are not
|
|
26
|
-
* installed. The keys are the part of the module name after
|
|
27
|
-
* `openmrs-module-`; e.g., `openmrs-module-fhir2` becomes `fhir2`.
|
|
28
|
-
*/
|
|
29
|
-
const backendDependencies = {
|
|
30
|
-
fhir2: "^1.2.0",
|
|
31
|
-
"webservices.rest": "^2.2.0",
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* This function performs any setup that should happen at microfrontend
|
|
36
|
-
* load-time (such as defining the config schema) and then returns an
|
|
37
|
-
* object which describes how the React application(s) should be
|
|
38
|
-
* rendered.
|
|
39
|
-
*
|
|
40
|
-
* In this example, our return object contains a single page definition.
|
|
41
|
-
* It tells the app shell that the default export of `greeter.tsx`
|
|
42
|
-
* should be rendered when the route matches `hello`. The full route
|
|
43
|
-
* will be `openmrsSpaBase() + 'hello'`, which is usually
|
|
44
|
-
* `/openmrs/spa/hello`.
|
|
45
|
-
*/
|
|
46
|
-
function setupOpenMRS() {
|
|
47
|
-
const moduleName = "@openmrs/esm-fast-data-entry";
|
|
22
|
+
export const root = getAsyncLifecycle(() => import("./Root"), options);
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
24
|
+
export const formsAppMenuLink = getAsyncLifecycle(
|
|
25
|
+
() => import("./forms-app-menu-link"),
|
|
26
|
+
options
|
|
27
|
+
);
|
|
53
28
|
|
|
29
|
+
export function startupApp() {
|
|
54
30
|
defineConfigSchema(moduleName, configSchema);
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
extensions: [
|
|
64
|
-
{
|
|
65
|
-
name: "forms-app-link",
|
|
66
|
-
slot: "app-menu-slot",
|
|
67
|
-
load: getAsyncLifecycle(() => import("./forms-app-menu-link"), options),
|
|
68
|
-
online: true,
|
|
69
|
-
offline: true,
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
};
|
|
32
|
+
registerBreadcrumbs([
|
|
33
|
+
{
|
|
34
|
+
path: `${window.spaBase}/forms`,
|
|
35
|
+
title: "Forms",
|
|
36
|
+
parent: `${window.spaBase}/home`,
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
73
39
|
}
|
|
74
|
-
|
|
75
|
-
export { backendDependencies, importTranslation, setupOpenMRS };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { CheckmarkOutline, WarningAlt } from "@carbon/react/icons";
|
|
2
|
+
import { SkeletonText } from "@carbon/react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import useGetPatient from "../hooks/useGetPatient";
|
|
5
|
+
import styles from "./styles.scss";
|
|
6
|
+
|
|
7
|
+
const CardContainer = ({ onClick = () => undefined, active, children }) => {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
onClick={onClick}
|
|
11
|
+
className={`${styles.cardContainer} ${!active && styles.inactiveCard}`}
|
|
12
|
+
role="button"
|
|
13
|
+
tabIndex={0}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const PatientCard = ({
|
|
21
|
+
patientUuid,
|
|
22
|
+
activePatientUuid,
|
|
23
|
+
editEncounter,
|
|
24
|
+
encounters,
|
|
25
|
+
}) => {
|
|
26
|
+
const patient = useGetPatient(patientUuid);
|
|
27
|
+
const givenName = patient?.name?.[0]?.given?.[0];
|
|
28
|
+
const familyName = patient?.name?.[0]?.family;
|
|
29
|
+
const identifier = patient?.identifier?.[0]?.value;
|
|
30
|
+
|
|
31
|
+
if (!patient) {
|
|
32
|
+
return (
|
|
33
|
+
<CardContainer active={true}>
|
|
34
|
+
<SkeletonText className={styles.skeletonText} />
|
|
35
|
+
</CardContainer>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const active = activePatientUuid === patientUuid;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<CardContainer
|
|
43
|
+
onClick={active ? () => undefined : () => editEncounter(patientUuid)}
|
|
44
|
+
active={active}
|
|
45
|
+
>
|
|
46
|
+
<div className={styles.patientInfo}>
|
|
47
|
+
<div className={styles.identifier}>{identifier}</div>
|
|
48
|
+
<div
|
|
49
|
+
className={`${styles.displayName} ${
|
|
50
|
+
active && styles.activeDisplayName
|
|
51
|
+
}`}
|
|
52
|
+
>
|
|
53
|
+
{givenName} {familyName}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
{patientUuid in encounters ? (
|
|
58
|
+
<CheckmarkOutline size={16} className={styles.statusSuccess} />
|
|
59
|
+
) : (
|
|
60
|
+
<WarningAlt size={16} className={styles.statusWarning} />
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
</CardContainer>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default PatientCard;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/colors';
|
|
3
|
+
// @use '@carbon/styles/scss/type';
|
|
4
|
+
// @import '~@openmrs/esm-styleguide/src/vars';
|
|
5
|
+
|
|
6
|
+
.cardContainer {
|
|
7
|
+
padding: spacing.$spacing-05;
|
|
8
|
+
display: flex;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.skeletonText {
|
|
13
|
+
max-width: 8rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.identifier {
|
|
17
|
+
font-weight: 300;
|
|
18
|
+
font-size: 0.8rem;
|
|
19
|
+
line-height: 0.9rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.displayName {
|
|
23
|
+
font-weight: bold;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.activeDisplayName {
|
|
27
|
+
color: colors.$blue-50;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.inactiveCard {
|
|
31
|
+
&:hover {
|
|
32
|
+
background-color: colors.$gray-30;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.patientInfo {
|
|
37
|
+
flex-grow: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.statusSuccess {
|
|
41
|
+
fill: colors.$green-60;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.statusWarning {
|
|
45
|
+
fill: colors.$yellow-40;
|
|
46
|
+
}
|
package/src/routes.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
|
+
"backendDependencies": {
|
|
4
|
+
"fhir2": "^1.2.0",
|
|
5
|
+
"webservices.rest": "^2.2.0"
|
|
6
|
+
},
|
|
7
|
+
"pages": [
|
|
8
|
+
{
|
|
9
|
+
"component": "root",
|
|
10
|
+
"routeRegex": "forms",
|
|
11
|
+
"online": true,
|
|
12
|
+
"offline": true
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"extensions": [
|
|
16
|
+
{
|
|
17
|
+
"name": "forms-app-link",
|
|
18
|
+
"slot": "app-menu-slot",
|
|
19
|
+
"component": "formsAppMenuLink",
|
|
20
|
+
"online": true,
|
|
21
|
+
"offline": true
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|