@openmrs/esm-fast-data-entry-app 1.0.0-pre.59 → 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 +21 -2
- 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 -1
- 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/{536.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/{935.js.LICENSE.txt → 961.js.LICENSE.txt} +6 -10
- 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 +500 -122
- package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.json +21 -18
- package/package.json +59 -62
- package/prettier.config.js +8 -0
- package/src/CancelModal.tsx +42 -0
- package/src/CompleteModal.tsx +35 -0
- package/src/FormBootstrap.tsx +45 -10
- package/src/Root.tsx +11 -9
- package/src/add-group-modal/AddGroupModal.tsx +249 -0
- package/src/add-group-modal/styles.scss +49 -0
- package/src/config-schema.ts +77 -16
- package/src/constant.ts +1 -1
- package/src/context/FormWorkflowContext.tsx +32 -33
- package/src/context/FormWorkflowReducer.ts +53 -67
- 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 +4 -16
- package/src/empty-state/EmptyState.tsx +16 -17
- package/src/empty-state/styles.scss +14 -14
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +89 -125
- package/src/{form-review-card → form-entry-workflow/form-review-card}/FormReviewCard.tsx +7 -7
- 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 +1 -1
- package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
- package/src/{patient-banner → form-entry-workflow/patient-banner}/PatientBanner.tsx +14 -27
- 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 +16 -16
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +37 -0
- package/src/form-entry-workflow/workflow-review/index.ts +3 -0
- package/src/{workflow-review → form-entry-workflow/workflow-review}/styles.scss +0 -4
- package/src/forms-app-menu-link.tsx +5 -7
- package/src/forms-page/FormsPage.tsx +48 -37
- 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 +1 -1
- package/src/forms-page/styles.scss +3 -5
- 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 +7 -5
- package/src/hooks/useForm.ts +56 -0
- package/src/hooks/useFormState.ts +3 -3
- package/src/hooks/useGetAllForms.ts +7 -15
- package/src/hooks/useGetEncounter.ts +3 -3
- package/src/hooks/useGetPatient.ts +3 -3
- 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 +12 -72
- package/src/patient-card/PatientCard.tsx +10 -20
- package/src/patient-card/index.ts +1 -1
- package/src/patient-card/styles.scss +8 -8
- 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 +57 -2
- 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/turbo.json +18 -0
- package/webpack.config.js +1 -1
- package/.editorconfig +0 -12
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -10
- package/.github/pull_request_template.md +0 -18
- package/.github/workflows/node.js.yml +0 -121
- package/.husky/pre-push +0 -1
- package/.prettierignore +0 -14
- package/dist/187.js +0 -1
- package/dist/247.js +0 -1
- package/dist/294.js +0 -2
- package/dist/312.js +0 -1
- package/dist/412.js +0 -1
- package/dist/536.js +0 -2
- package/dist/574.js +0 -1
- package/dist/592.js +0 -1
- package/dist/595.js +0 -2
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/776.js +0 -1
- package/dist/804.js +0 -1
- package/dist/880.js +0 -2
- package/dist/880.js.LICENSE.txt +0 -20
- package/dist/906.js +0 -1
- package/dist/935.js +0 -2
- package/dist/990.js +0 -1
- package/dist/openmrs-esm-fast-data-entry-app.old +0 -1
- package/src/declarations.d.tsx +0 -2
- package/src/form-review-card/index.ts +0 -3
- package/src/form-review-card/styles.scss +0 -38
- package/src/forms-table/FormsTable.tsx +0 -123
- package/src/forms-table/index.ts +0 -3
- package/src/forms-table/styles.scss +0 -20
- package/src/patient-banner/PatientBanner.test.tsx +0 -9
- package/src/patient-banner/index.ts +0 -3
- package/src/patient-banner/styles.scss +0 -44
- package/src/patient-search-header/PatientSearchHeader.tsx +0 -61
- package/src/patient-search-header/index.ts +0 -3
- package/src/patient-search-header/styles.scss +0 -21
- package/src/workflow-review/WorkflowReview.tsx +0 -35
- package/src/workflow-review/index.ts +0 -3
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
userHasAccess,
|
|
4
|
-
useSession,
|
|
5
|
-
} from "@openmrs/esm-framework";
|
|
6
|
-
import useSWR from "swr";
|
|
1
|
+
import { openmrsFetch, userHasAccess, useSession, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import useSWR from 'swr';
|
|
7
3
|
|
|
8
4
|
const customFormRepresentation =
|
|
9
|
-
|
|
5
|
+
'(uuid,name,display,encounterType:(uuid,name,viewPrivilege,editPrivilege),version,published,retired,resources:(uuid,name,dataType,valueReference))';
|
|
10
6
|
|
|
11
|
-
const formEncounterUrl =
|
|
12
|
-
const formEncounterUrlPoc =
|
|
7
|
+
const formEncounterUrl = `${restBaseUrl}/form?v=custom:${customFormRepresentation}`;
|
|
8
|
+
const formEncounterUrlPoc = `${restBaseUrl}/form?v=custom:${customFormRepresentation}&q=poc`;
|
|
13
9
|
|
|
14
10
|
export function useGetAllForms(cachedOfflineFormsOnly = false) {
|
|
15
11
|
const session = useSession();
|
|
@@ -24,7 +20,7 @@ export function useGetAllForms(cachedOfflineFormsOnly = false) {
|
|
|
24
20
|
// forms should be published
|
|
25
21
|
form.published &&
|
|
26
22
|
// forms should not be component forms
|
|
27
|
-
!/component/i.test(form.name)
|
|
23
|
+
!/component/i.test(form.name),
|
|
28
24
|
// user should have privileges to edit forms
|
|
29
25
|
) ?? [];
|
|
30
26
|
|
|
@@ -32,11 +28,7 @@ export function useGetAllForms(cachedOfflineFormsOnly = false) {
|
|
|
32
28
|
});
|
|
33
29
|
|
|
34
30
|
return {
|
|
35
|
-
forms: data?.filter((form) =>
|
|
36
|
-
Boolean(
|
|
37
|
-
userHasAccess(form.encounterType?.editPrivilege?.display, session?.user)
|
|
38
|
-
)
|
|
39
|
-
),
|
|
31
|
+
forms: data?.filter((form) => Boolean(userHasAccess(form.encounterType?.editPrivilege?.display, session?.user))),
|
|
40
32
|
isLoading: !error && !data,
|
|
41
33
|
error,
|
|
42
34
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { openmrsFetch } from
|
|
2
|
-
import useSWR from
|
|
1
|
+
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import useSWR from 'swr';
|
|
3
3
|
|
|
4
|
-
const encounterUrl =
|
|
4
|
+
const encounterUrl = `${restBaseUrl}/encounter/`;
|
|
5
5
|
|
|
6
6
|
const useGetEncounter = (encounterUuid) => {
|
|
7
7
|
const url = `${encounterUrl}${encounterUuid}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fetchCurrentPatient } from
|
|
2
|
-
import { useEffect, useState } from
|
|
1
|
+
import { fetchCurrentPatient } from '@openmrs/esm-framework';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
const useGetPatient = (patientUuid) => {
|
|
5
5
|
const [patient, setPatient] = useState(null);
|
|
@@ -14,7 +14,7 @@ const useGetPatient = (patientUuid) => {
|
|
|
14
14
|
|
|
15
15
|
const getPatient = async (uuid) => {
|
|
16
16
|
const result = await fetchCurrentPatient(uuid);
|
|
17
|
-
setPatient(result
|
|
17
|
+
setPatient(result);
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
return patient;
|
|
@@ -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,87 +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
|
-
getAsyncLifecycle,
|
|
10
|
-
defineConfigSchema,
|
|
11
|
-
registerBreadcrumbs,
|
|
12
|
-
} from "@openmrs/esm-framework";
|
|
13
|
-
import { configSchema } from "./config-schema";
|
|
4
|
+
const moduleName = '@openmrs/esm-fast-data-entry-app';
|
|
14
5
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* see in the directory structure).
|
|
19
|
-
*/
|
|
20
|
-
const importTranslation = require.context(
|
|
21
|
-
"../translations",
|
|
22
|
-
false,
|
|
23
|
-
/.json$/,
|
|
24
|
-
"lazy"
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* This tells the app shell what versions of what OpenMRS backend modules
|
|
29
|
-
* are expected. Warnings will appear if suitable modules are not
|
|
30
|
-
* installed. The keys are the part of the module name after
|
|
31
|
-
* `openmrs-module-`; e.g., `openmrs-module-fhir2` becomes `fhir2`.
|
|
32
|
-
*/
|
|
33
|
-
const backendDependencies = {
|
|
34
|
-
fhir2: "^1.2.0",
|
|
35
|
-
"webservices.rest": "^2.2.0",
|
|
6
|
+
const options = {
|
|
7
|
+
featureName: 'fast-data-entry-app',
|
|
8
|
+
moduleName,
|
|
36
9
|
};
|
|
37
10
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* object which describes how the React application(s) should be
|
|
42
|
-
* rendered.
|
|
43
|
-
*
|
|
44
|
-
* In this example, our return object contains a single page definition.
|
|
45
|
-
* It tells the app shell that the default export of `greeter.tsx`
|
|
46
|
-
* should be rendered when the route matches `hello`. The full route
|
|
47
|
-
* will be `openmrsSpaBase() + 'hello'`, which is usually
|
|
48
|
-
* `/openmrs/spa/hello`.
|
|
49
|
-
*/
|
|
50
|
-
function setupOpenMRS() {
|
|
51
|
-
const moduleName = "@openmrs/esm-fast-data-entry-app";
|
|
11
|
+
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
12
|
+
|
|
13
|
+
export const root = getAsyncLifecycle(() => import('./Root'), options);
|
|
52
14
|
|
|
53
|
-
|
|
54
|
-
featureName: "fast-data-entry-app",
|
|
55
|
-
moduleName,
|
|
56
|
-
};
|
|
15
|
+
export const formsAppMenuLink = getAsyncLifecycle(() => import('./forms-app-menu-link'), options);
|
|
57
16
|
|
|
17
|
+
export function startupApp() {
|
|
58
18
|
defineConfigSchema(moduleName, configSchema);
|
|
59
19
|
|
|
60
20
|
registerBreadcrumbs([
|
|
61
21
|
{
|
|
62
22
|
path: `${window.spaBase}/forms`,
|
|
63
|
-
title:
|
|
23
|
+
title: 'Forms',
|
|
64
24
|
parent: `${window.spaBase}/home`,
|
|
65
25
|
},
|
|
66
26
|
]);
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
pages: [
|
|
70
|
-
{
|
|
71
|
-
load: getAsyncLifecycle(() => import("./Root"), options),
|
|
72
|
-
route: "forms",
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
extensions: [
|
|
76
|
-
{
|
|
77
|
-
name: "forms-app-link",
|
|
78
|
-
slot: "app-menu-slot",
|
|
79
|
-
load: getAsyncLifecycle(() => import("./forms-app-menu-link"), options),
|
|
80
|
-
online: true,
|
|
81
|
-
offline: true,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
};
|
|
85
27
|
}
|
|
86
|
-
|
|
87
|
-
export { backendDependencies, importTranslation, setupOpenMRS };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SkeletonText } from
|
|
3
|
-
import React
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import styles from "./styles.scss";
|
|
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';
|
|
7
6
|
|
|
8
7
|
const CardContainer = ({ onClick = () => undefined, active, children }) => {
|
|
9
8
|
return (
|
|
@@ -18,9 +17,7 @@ const CardContainer = ({ onClick = () => undefined, active, children }) => {
|
|
|
18
17
|
);
|
|
19
18
|
};
|
|
20
19
|
|
|
21
|
-
const PatientCard = ({ patientUuid }) => {
|
|
22
|
-
const { activePatientUuid, editEncounter, encounters } =
|
|
23
|
-
useContext(FormWorkflowContext);
|
|
20
|
+
const PatientCard = ({ patientUuid, activePatientUuid, editEncounter, encounters }) => {
|
|
24
21
|
const patient = useGetPatient(patientUuid);
|
|
25
22
|
const givenName = patient?.name?.[0]?.given?.[0];
|
|
26
23
|
const familyName = patient?.name?.[0]?.family;
|
|
@@ -37,25 +34,18 @@ const PatientCard = ({ patientUuid }) => {
|
|
|
37
34
|
const active = activePatientUuid === patientUuid;
|
|
38
35
|
|
|
39
36
|
return (
|
|
40
|
-
<CardContainer
|
|
41
|
-
onClick={active ? () => undefined : () => editEncounter(patientUuid)}
|
|
42
|
-
active={active}
|
|
43
|
-
>
|
|
37
|
+
<CardContainer onClick={active ? () => undefined : () => editEncounter(patientUuid)} active={active}>
|
|
44
38
|
<div className={styles.patientInfo}>
|
|
45
39
|
<div className={styles.identifier}>{identifier}</div>
|
|
46
|
-
<div
|
|
47
|
-
className={`${styles.displayName} ${
|
|
48
|
-
active && styles.activeDisplayName
|
|
49
|
-
}`}
|
|
50
|
-
>
|
|
40
|
+
<div className={`${styles.displayName} ${active && styles.activeDisplayName}`}>
|
|
51
41
|
{givenName} {familyName}
|
|
52
42
|
</div>
|
|
53
43
|
</div>
|
|
54
44
|
<div>
|
|
55
45
|
{patientUuid in encounters ? (
|
|
56
|
-
<
|
|
46
|
+
<CheckmarkOutline size={16} className={styles.statusSuccess} />
|
|
57
47
|
) : (
|
|
58
|
-
<
|
|
48
|
+
<WarningAlt size={16} className={styles.statusWarning} />
|
|
59
49
|
)}
|
|
60
50
|
</div>
|
|
61
51
|
</CardContainer>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
3
|
-
@import "~carbon-components/src/globals/scss/mixins";
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
4
3
|
|
|
5
4
|
.cardContainer {
|
|
6
|
-
padding:
|
|
5
|
+
padding: layout.$spacing-05;
|
|
7
6
|
display: flex;
|
|
7
|
+
cursor: pointer;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
.skeletonText {
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.activeDisplayName {
|
|
25
|
-
color:
|
|
25
|
+
color: colors.$blue-50;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
.inactiveCard {
|
|
29
29
|
&:hover {
|
|
30
|
-
background-color:
|
|
30
|
+
background-color: colors.$gray-30;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
.statusSuccess {
|
|
39
|
-
fill:
|
|
39
|
+
fill: colors.$green-60;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
.statusWarning {
|
|
43
|
-
fill:
|
|
43
|
+
fill: colors.$yellow-40;
|
|
44
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';
|