@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.0
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/__mocks__/react-i18next.js +9 -14
- package/dist/101.js +1 -0
- package/dist/101.js.map +1 -0
- package/dist/132.js +1 -0
- package/dist/188.js +1 -0
- package/dist/188.js.map +1 -0
- package/dist/197.js +1 -0
- package/dist/219.js +1 -0
- package/dist/219.js.map +1 -0
- package/dist/221.js +1 -0
- package/dist/221.js.map +1 -0
- package/dist/259.js +1 -0
- package/dist/259.js.map +1 -0
- package/dist/29.js +2 -0
- package/dist/29.js.LICENSE.txt +3 -0
- package/dist/29.js.map +1 -0
- package/dist/300.js +1 -0
- package/dist/326.js +1 -0
- package/dist/326.js.map +1 -0
- package/dist/335.js +1 -0
- package/dist/367.js +1 -0
- package/dist/367.js.map +1 -0
- package/dist/480.js +1 -0
- package/dist/540.js +2 -0
- package/dist/{382.js.LICENSE.txt → 540.js.LICENSE.txt} +3 -2
- package/dist/540.js.map +1 -0
- package/dist/55.js +1 -0
- package/dist/564.js +1 -0
- package/dist/564.js.map +1 -0
- package/dist/602.js +1 -0
- package/dist/602.js.map +1 -0
- package/dist/626.js +2 -0
- package/dist/{294.js.LICENSE.txt → 626.js.LICENSE.txt} +3 -8
- package/dist/626.js.map +1 -0
- package/dist/652.js +1 -0
- package/dist/685.js +1 -0
- package/dist/685.js.map +1 -0
- package/dist/773.js +2 -0
- package/dist/773.js.LICENSE.txt +32 -0
- package/dist/773.js.map +1 -0
- package/dist/893.js +1 -0
- package/dist/893.js.map +1 -0
- package/dist/91.js +1 -0
- package/dist/91.js.map +1 -0
- package/dist/941.js +2 -0
- package/dist/941.js.LICENSE.txt +30 -0
- package/dist/941.js.map +1 -0
- package/dist/961.js +2 -0
- package/dist/{735.js.LICENSE.txt → 961.js.LICENSE.txt} +6 -16
- package/dist/961.js.map +1 -0
- package/dist/99.js +1 -0
- package/dist/99.js.map +1 -0
- package/dist/991.js +1 -0
- package/dist/991.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 +537 -95
- 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 +101 -106
- package/prettier.config.js +8 -0
- package/src/CancelModal.tsx +42 -0
- package/src/CompleteModal.tsx +35 -0
- package/src/FormBootstrap.tsx +179 -0
- package/src/Root.tsx +11 -5
- package/src/add-group-modal/AddGroupModal.tsx +249 -0
- package/src/add-group-modal/styles.scss +49 -0
- package/src/config-schema.ts +124 -31
- package/src/constant.ts +1 -1
- package/src/context/FormWorkflowContext.tsx +113 -0
- package/src/context/FormWorkflowReducer.ts +263 -0
- package/src/context/GroupFormWorkflowContext.tsx +155 -0
- package/src/context/GroupFormWorkflowReducer.ts +405 -0
- package/src/declarations.d.ts +4 -0
- package/src/empty-state/EmptyDataIllustration.tsx +39 -0
- package/src/empty-state/EmptyState.tsx +28 -0
- package/src/empty-state/styles.scss +55 -0
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +184 -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 +37 -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 +73 -0
- package/src/form-entry-workflow/patient-banner/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/styles.scss +44 -0
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +54 -0
- package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
- package/src/form-entry-workflow/patient-search-header/styles.scss +25 -0
- package/src/form-entry-workflow/styles.scss +63 -0
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +37 -0
- package/src/form-entry-workflow/workflow-review/index.ts +3 -0
- package/src/form-entry-workflow/workflow-review/styles.scss +30 -0
- package/src/forms-app-menu-link.tsx +6 -7
- package/src/forms-page/FormsPage.tsx +106 -0
- package/src/forms-page/forms-table/FormsTable.tsx +117 -0
- package/src/forms-page/forms-table/index.ts +3 -0
- package/src/forms-page/forms-table/styles.scss +19 -0
- package/src/forms-page/index.ts +3 -0
- package/src/forms-page/styles.scss +9 -0
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
- package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +207 -0
- package/src/group-form-entry-workflow/SessionDetailsForm.tsx +154 -0
- package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +99 -0
- package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +130 -0
- package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
- package/src/group-form-entry-workflow/configurable-questions/ConfigurableQuestionsSection.tsx +41 -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 +55 -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 +128 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +66 -0
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +134 -0
- package/src/group-form-entry-workflow/group-search/compact-group-result.scss +63 -0
- package/src/group-form-entry-workflow/group-search/compact-group-search.scss +34 -0
- package/src/group-form-entry-workflow/group-search/group-search.scss +93 -0
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +72 -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 +94 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/useForm.ts +56 -0
- package/src/hooks/useFormState.ts +23 -0
- package/src/hooks/useGetAllForms.ts +37 -0
- package/src/hooks/useGetEncounter.ts +21 -0
- package/src/hooks/useGetPatient.ts +23 -0
- package/src/hooks/useGetPatients.ts +32 -0
- package/src/hooks/useGetSystemSetting.ts +36 -0
- package/src/hooks/useKeyPress.ts +31 -0
- package/src/hooks/usePostEndpoint.ts +76 -0
- package/src/hooks/useSearchEndpoint.ts +103 -0
- package/src/hooks/useStartVisit.ts +82 -0
- package/src/index.ts +18 -66
- package/src/patient-card/PatientCard.tsx +55 -0
- package/src/patient-card/index.ts +3 -0
- package/src/patient-card/styles.scss +44 -0
- package/src/routes.json +24 -0
- package/src/setup-tests.ts +1 -1
- package/src/types.ts +20 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/am.json +75 -0
- package/translations/ar.json +75 -0
- package/translations/en.json +75 -4
- package/translations/es.json +75 -0
- package/translations/fr.json +75 -0
- package/translations/he.json +75 -0
- package/translations/km.json +75 -0
- package/tsconfig.json +26 -23
- package/turbo.json +18 -0
- package/webpack.config.js +1 -1
- 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/294.js +0 -3
- package/dist/294.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/415.js +0 -2
- package/dist/415.js.map +0 -1
- package/dist/574.js +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/declarations.d.tsx +0 -2
- 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,32 @@
|
|
|
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(uuids.map(async (uuid) => await fetchCurrentPatient(uuid)));
|
|
21
|
+
setPatients(results);
|
|
22
|
+
setIsLoading(false);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('Error fetching patients:', error);
|
|
25
|
+
setIsLoading(false);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return { patients, isLoading };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default useGetPatients;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { openmrsFetch, restBaseUrl } 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(`${restBaseUrl}/systemsetting?q=${settingId}&v=default`).then(onResult).catch(onError);
|
|
23
|
+
}, [onError, onResult, settingId]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
getSetting();
|
|
27
|
+
}, [getSetting]);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
result,
|
|
31
|
+
error,
|
|
32
|
+
isSubmitting,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
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, restBaseUrl } 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: `${restBaseUrl}/visit` });
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const usePostCohort = () => {
|
|
73
|
+
return usePostEndpoint({ endpointUrl: `${restBaseUrl}/cohortm/cohort` });
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { usePostEndpoint, usePostVisit, usePostCohort };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { openmrsFetch, type FetchResponse, restBaseUrl } 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
|
+
Array<
|
|
15
|
+
FetchResponse<{
|
|
16
|
+
results: Array<Record<string, unknown>>;
|
|
17
|
+
links: Array<{
|
|
18
|
+
rel: 'prev' | 'next';
|
|
19
|
+
}>;
|
|
20
|
+
}>
|
|
21
|
+
>
|
|
22
|
+
>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SearchInfiniteProps {
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
searchTerm: string;
|
|
28
|
+
parameters?: Record<string, unknown> | undefined;
|
|
29
|
+
searching: boolean;
|
|
30
|
+
resultsToFetch?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const useSearchEndpointInfinite = (arg0: SearchInfiniteProps): SearchResponse => {
|
|
34
|
+
const { baseUrl, searchTerm, parameters, searching = true, resultsToFetch = 10 } = arg0;
|
|
35
|
+
|
|
36
|
+
const getUrl = useCallback(
|
|
37
|
+
(
|
|
38
|
+
page: number,
|
|
39
|
+
prevPageData: FetchResponse<{
|
|
40
|
+
results: Array<Record<string, unknown>>;
|
|
41
|
+
links: Array<{ rel: 'prev' | 'next' }>;
|
|
42
|
+
}>,
|
|
43
|
+
) => {
|
|
44
|
+
if (prevPageData && !prevPageData?.data?.links.some((link) => link.rel === 'next')) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
let url = `${baseUrl}?q=${searchTerm}`;
|
|
48
|
+
const params = {
|
|
49
|
+
// merge passed parameters and default parameters
|
|
50
|
+
// this way the defaults can be overriden if needed
|
|
51
|
+
totalCount: true,
|
|
52
|
+
limit: resultsToFetch,
|
|
53
|
+
...parameters,
|
|
54
|
+
};
|
|
55
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
56
|
+
// don't send null parmeters
|
|
57
|
+
if (value !== null && value !== undefined) {
|
|
58
|
+
url += `&${key}=${value}`;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
if (page) {
|
|
62
|
+
url += `&startIndex=${page * resultsToFetch}`;
|
|
63
|
+
}
|
|
64
|
+
return url;
|
|
65
|
+
},
|
|
66
|
+
[baseUrl, searchTerm, parameters, resultsToFetch],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const { data, isValidating, setSize, error, size } = useSWRInfinite<
|
|
70
|
+
FetchResponse<{
|
|
71
|
+
results: Array<Record<string, unknown>>;
|
|
72
|
+
links: Array<{ rel: 'prev' | 'next' }>;
|
|
73
|
+
totalCount: number;
|
|
74
|
+
}>,
|
|
75
|
+
Error
|
|
76
|
+
>(searching ? getUrl : null, openmrsFetch);
|
|
77
|
+
|
|
78
|
+
const results = useMemo(
|
|
79
|
+
() => ({
|
|
80
|
+
data: data ? [].concat(...(data?.map((resp) => resp?.data?.results) ?? [])) : null,
|
|
81
|
+
isLoading: !data && !error,
|
|
82
|
+
error,
|
|
83
|
+
hasMore: data?.length ? !!data[data.length - 1].data?.links?.some((link) => link.rel === 'next') : false,
|
|
84
|
+
loadingNewData: isValidating,
|
|
85
|
+
setPage: setSize,
|
|
86
|
+
currentPage: size,
|
|
87
|
+
totalResults: data?.[0]?.data?.totalCount,
|
|
88
|
+
}),
|
|
89
|
+
[data, isValidating, error, setSize, size],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return results;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const useSearchCohortInfinite = ({ ...props }: SearchInfiniteProps): SearchResponse => {
|
|
96
|
+
return useSearchEndpointInfinite({
|
|
97
|
+
baseUrl: `${restBaseUrl}/cohortm/cohort`,
|
|
98
|
+
resultsToFetch: 10,
|
|
99
|
+
...props,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export { useSearchEndpointInfinite, useSearchCohortInfinite };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { showNotification, showToast, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
4
|
+
|
|
5
|
+
const useStartVisit = ({ showSuccessNotification = true, showErrorNotification = true }) => {
|
|
6
|
+
const { t } = useTranslation();
|
|
7
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
8
|
+
const [success, setSuccess] = useState(null);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
|
|
11
|
+
const onSave = useCallback(
|
|
12
|
+
(result) => {
|
|
13
|
+
setIsSubmitting(false);
|
|
14
|
+
setError(false);
|
|
15
|
+
setSuccess(result);
|
|
16
|
+
if (showSuccessNotification) {
|
|
17
|
+
showToast({
|
|
18
|
+
critical: true,
|
|
19
|
+
kind: 'success',
|
|
20
|
+
description: t('visitStartedSuccessfully', `${result?.data?.visitType?.display} started successfully`),
|
|
21
|
+
title: t('visitStarted', 'Visit started'),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
[t, showSuccessNotification],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const onError = useCallback(
|
|
29
|
+
(error) => {
|
|
30
|
+
setIsSubmitting(false);
|
|
31
|
+
setSuccess(false);
|
|
32
|
+
setError(error);
|
|
33
|
+
if (showErrorNotification) {
|
|
34
|
+
showNotification({
|
|
35
|
+
title: t('startVisitError', 'Error starting visit'),
|
|
36
|
+
kind: 'error',
|
|
37
|
+
critical: true,
|
|
38
|
+
description: error?.message,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[t, showErrorNotification],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const saveVisit = useCallback(
|
|
46
|
+
(data) => {
|
|
47
|
+
const payload = {
|
|
48
|
+
patient: data.patientUuid,
|
|
49
|
+
startDatetime: data.startDatetime,
|
|
50
|
+
stopDatetime: data.stopDatetime,
|
|
51
|
+
visitType: data.visitType,
|
|
52
|
+
location: data.location,
|
|
53
|
+
};
|
|
54
|
+
openmrsFetch(`${restBaseUrl}/visit`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
body: payload,
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
})
|
|
59
|
+
.then(onSave)
|
|
60
|
+
.catch(onError);
|
|
61
|
+
},
|
|
62
|
+
[onError, onSave],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const updateEncounter = useCallback((data) => {
|
|
66
|
+
openmrsFetch(`${restBaseUrl}/encounter/` + data.uuid, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
body: { visit: data.visit },
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
});
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
saveVisit,
|
|
75
|
+
updateEncounter,
|
|
76
|
+
success,
|
|
77
|
+
error,
|
|
78
|
+
isSubmitting,
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default useStartVisit;
|
package/src/index.ts
CHANGED
|
@@ -1,75 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* important features of this microfrontend to the app shell. It
|
|
4
|
-
* connects the app shell to the React application(s) that make up this
|
|
5
|
-
* microfrontend.
|
|
6
|
-
*/
|
|
1
|
+
import { getAsyncLifecycle, defineConfigSchema, registerBreadcrumbs } from '@openmrs/esm-framework';
|
|
2
|
+
import { configSchema } from './config-schema';
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
import { configSchema } from "./config-schema";
|
|
4
|
+
const moduleName = '@openmrs/esm-fast-data-entry-app';
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* see in the directory structure).
|
|
15
|
-
*/
|
|
16
|
-
const importTranslation = require.context(
|
|
17
|
-
"../translations",
|
|
18
|
-
false,
|
|
19
|
-
/.json$/,
|
|
20
|
-
"lazy"
|
|
21
|
-
);
|
|
22
|
-
|
|
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",
|
|
6
|
+
const options = {
|
|
7
|
+
featureName: 'fast-data-entry-app',
|
|
8
|
+
moduleName,
|
|
32
9
|
};
|
|
33
10
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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";
|
|
11
|
+
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
12
|
+
|
|
13
|
+
export const root = getAsyncLifecycle(() => import('./Root'), options);
|
|
48
14
|
|
|
49
|
-
|
|
50
|
-
featureName: "fast-data-entry",
|
|
51
|
-
moduleName,
|
|
52
|
-
};
|
|
15
|
+
export const formsAppMenuLink = getAsyncLifecycle(() => import('./forms-app-menu-link'), options);
|
|
53
16
|
|
|
17
|
+
export function startupApp() {
|
|
54
18
|
defineConfigSchema(moduleName, configSchema);
|
|
55
19
|
|
|
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
|
-
};
|
|
20
|
+
registerBreadcrumbs([
|
|
21
|
+
{
|
|
22
|
+
path: `${window.spaBase}/forms`,
|
|
23
|
+
title: 'Forms',
|
|
24
|
+
parent: `${window.spaBase}/home`,
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
73
27
|
}
|
|
74
|
-
|
|
75
|
-
export { backendDependencies, importTranslation, setupOpenMRS };
|
|
@@ -0,0 +1,55 @@
|
|
|
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 = ({ patientUuid, activePatientUuid, editEncounter, encounters }) => {
|
|
21
|
+
const patient = useGetPatient(patientUuid);
|
|
22
|
+
const givenName = patient?.name?.[0]?.given?.[0];
|
|
23
|
+
const familyName = patient?.name?.[0]?.family;
|
|
24
|
+
const identifier = patient?.identifier?.[0]?.value;
|
|
25
|
+
|
|
26
|
+
if (!patient) {
|
|
27
|
+
return (
|
|
28
|
+
<CardContainer active={true}>
|
|
29
|
+
<SkeletonText className={styles.skeletonText} />
|
|
30
|
+
</CardContainer>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const active = activePatientUuid === patientUuid;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<CardContainer onClick={active ? () => undefined : () => editEncounter(patientUuid)} active={active}>
|
|
38
|
+
<div className={styles.patientInfo}>
|
|
39
|
+
<div className={styles.identifier}>{identifier}</div>
|
|
40
|
+
<div className={`${styles.displayName} ${active && styles.activeDisplayName}`}>
|
|
41
|
+
{givenName} {familyName}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
{patientUuid in encounters ? (
|
|
46
|
+
<CheckmarkOutline size={16} className={styles.statusSuccess} />
|
|
47
|
+
) : (
|
|
48
|
+
<WarningAlt size={16} className={styles.statusWarning} />
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</CardContainer>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default PatientCard;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
|
|
4
|
+
.cardContainer {
|
|
5
|
+
padding: layout.$spacing-05;
|
|
6
|
+
display: flex;
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.skeletonText {
|
|
11
|
+
max-width: 8rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.identifier {
|
|
15
|
+
font-weight: 300;
|
|
16
|
+
font-size: 0.8rem;
|
|
17
|
+
line-height: 0.9rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.displayName {
|
|
21
|
+
font-weight: bold;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.activeDisplayName {
|
|
25
|
+
color: colors.$blue-50;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.inactiveCard {
|
|
29
|
+
&:hover {
|
|
30
|
+
background-color: colors.$gray-30;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.patientInfo {
|
|
35
|
+
flex-grow: 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.statusSuccess {
|
|
39
|
+
fill: colors.$green-60;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.statusWarning {
|
|
43
|
+
fill: colors.$yellow-40;
|
|
44
|
+
}
|
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",
|
|
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
|
+
}
|
package/src/setup-tests.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import '@testing-library/jest-dom/extend-expect';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface Concept {
|
|
2
|
+
uuid: string;
|
|
3
|
+
display: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SpecificQuestion {
|
|
7
|
+
question: {
|
|
8
|
+
id: string;
|
|
9
|
+
display: string;
|
|
10
|
+
};
|
|
11
|
+
answers: Array<{
|
|
12
|
+
value: string;
|
|
13
|
+
display: string;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SpecificQuestionConfig {
|
|
18
|
+
forms: Array<string>;
|
|
19
|
+
questionId: string;
|
|
20
|
+
}
|