@kenyaemr/esm-express-workflow-app 5.4.4-pre.299 → 5.4.4-pre.3
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/.turbo/turbo-build.log +3 -3
- package/dist/126.js +1 -0
- package/dist/126.js.map +1 -0
- package/dist/2024.js +1 -1
- package/dist/2024.js.map +1 -1
- package/dist/2225.js +1 -1
- package/dist/2225.js.map +1 -1
- package/dist/2386.js +1 -0
- package/dist/2386.js.map +1 -0
- package/dist/2438.js +1 -1
- package/dist/2438.js.map +1 -1
- package/dist/4504.js +1 -0
- package/dist/4504.js.map +1 -0
- package/dist/4670.js +1 -0
- package/dist/4670.js.map +1 -0
- package/dist/4682.js +1 -1
- package/dist/4682.js.map +1 -1
- package/dist/5047.js +1 -0
- package/dist/5047.js.map +1 -0
- package/dist/586.js.map +1 -1
- package/dist/5986.js +1 -1
- package/dist/5986.js.map +1 -1
- package/dist/6075.js +1 -0
- package/dist/6075.js.map +1 -0
- package/dist/6328.js +1 -1
- package/dist/6328.js.map +1 -1
- package/dist/64.js +1 -0
- package/dist/64.js.map +1 -0
- package/dist/6456.js +1 -1
- package/dist/6720.js +1 -0
- package/dist/6720.js.map +1 -0
- package/dist/7607.js +1 -1
- package/dist/8566.js +1 -0
- package/dist/8566.js.map +1 -0
- package/dist/8727.js +1 -1
- package/dist/8799.js +1 -0
- package/dist/8799.js.map +1 -0
- package/dist/8911.js +1 -1
- package/dist/8911.js.map +1 -1
- package/dist/9157.js +1 -1
- package/dist/9157.js.map +1 -1
- package/dist/9161.js +1 -0
- package/dist/9161.js.map +1 -0
- package/dist/9182.js +1 -1
- package/dist/9343.js +1 -0
- package/dist/9343.js.map +1 -0
- package/dist/9535.js +1 -1
- package/dist/9535.js.map +1 -1
- package/dist/9834.js +1 -0
- package/dist/{4432.js.map → 9834.js.map} +1 -1
- package/dist/kenyaemr-esm-express-workflow-app.js +2 -2
- package/dist/kenyaemr-esm-express-workflow-app.js.buildmanifest.json +294 -151
- package/dist/kenyaemr-esm-express-workflow-app.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/components/admissions/index.tsx +0 -1
- package/src/components/anaesthetic/anaesthetic.component.tsx +10 -4
- package/src/components/anaesthetic/forms/interoperative-record-drug-given-form.component.tsx +2 -26
- package/src/components/anaesthetic/graphs/interoperative-record-drug-given-graph-wrapper.component.tsx +66 -25
- package/src/components/anaesthetic/resources/anaesthetic.resource.ts +2 -2
- package/src/components/consultation/clinical-encounter/clinical-encounter.component.tsx +4 -20
- package/src/components/consultation/index.ts +1 -1
- package/src/components/consultation/tabs/index.ts +67 -0
- package/src/components/facility-dashboard/components/header/home-header.component.tsx +8 -13
- package/src/components/laboratory/index.ts +11 -0
- package/src/components/laboratory/lab-table.component.tsx +25 -12
- package/src/components/laboratory/laboratory-tabs.component.tsx +3 -17
- package/src/components/pharmacy/index.ts +1 -1
- package/src/components/pharmacy/pharmacy-tabs.component.tsx +4 -20
- package/src/components/pharmacy/tabs/index.ts +21 -0
- package/src/components/procedures/index.ts +16 -0
- package/src/components/procedures/procedures-table.component.tsx +33 -15
- package/src/components/procedures/procedures-tabs.component.tsx +3 -22
- package/src/components/radiology-and-imaging/index.ts +11 -0
- package/src/components/radiology-and-imaging/radiology-and-imaging-table.component.tsx +35 -15
- package/src/components/radiology-and-imaging/radiology-and-imaging.component.tsx +3 -19
- package/src/components/registration/card/Local-card/local-card.component.tsx +3 -3
- package/src/components/registration/dependants/dependants.component.tsx +6 -1
- package/src/components/registration/end-visit-modal/end-visit-dialog.modal.tsx +320 -0
- package/src/components/registration/end-visit-modal/end-visit-dialog.scss +12 -0
- package/src/components/registration/end-visit-modal/end-visit.resource.tsx +59 -0
- package/src/components/registration/end-visit-modal/sha-end-visit.resource.ts +285 -0
- package/src/components/registration/start-visit-form/overflow-menu-extension/custom-end-active-visit.extension.tsx +44 -0
- package/src/components/registration/start-visit-form/start-visit-workspace/exported-visit-form.workspace.tsx +16 -11
- package/src/components/registration/type/index.ts +101 -0
- package/src/config-schema.ts +23 -1
- package/src/index.ts +36 -17
- package/src/routes.json +205 -12
- package/src/shared/otp-verification/index.ts +3 -0
- package/src/shared/otp-verification/otp-verification.modal.tsx +51 -17
- package/src/shared/patient-chart/patient-summary-dashboard/patient-summary-dashboard.component.tsx +5 -73
- package/src/shared/tabs/create-tab-extension.tsx +33 -0
- package/src/shared/tabs/extension-tabs.component.tsx +51 -33
- package/src/shared/utils/index.ts +6 -6
- package/translations/am.json +32 -24
- package/translations/en.json +23 -16
- package/translations/fr.json +23 -16
- package/translations/sw.json +23 -21
- package/Failed to delete encounter/am.json +0 -4
- package/Failed to delete encounter/en.json +0 -4
- package/Failed to delete encounter/fr.json +0 -4
- package/Failed to delete encounter/sw.json +0 -4
- package/dist/2332.js +0 -1
- package/dist/2332.js.map +0 -1
- package/dist/4432.js +0 -1
- package/dist/4666.js +0 -1
- package/dist/4666.js.map +0 -1
- package/dist/5733.js +0 -1
- package/dist/5733.js.map +0 -1
- package/dist/644.js +0 -1
- package/dist/644.js.map +0 -1
- package/dist/655.js +0 -1
- package/dist/655.js.map +0 -1
- package/dist/70.js +0 -1
- package/dist/70.js.map +0 -1
- package/dist/9387.js +0 -1
- package/dist/9387.js.map +0 -1
- package/src/components/admissions/admission-medication-summary.tsx +0 -302
- package/src/components/admissions/admissions-dashboard.utils.test.ts +0 -48
- package/src/components/admissions/admissions-dashboard.utils.tsx +0 -63
- package/src/shared/orders/OrdersTabs.tsx +0 -76
- /package/src/shared/{patient-chart/patient-summary-dashboard → tabs}/patient-summary-dashboard.scss +0 -0
|
@@ -10,8 +10,6 @@ import {
|
|
|
10
10
|
ModalHeader,
|
|
11
11
|
NumberInput,
|
|
12
12
|
TextArea,
|
|
13
|
-
TextInput,
|
|
14
|
-
IconButton,
|
|
15
13
|
} from '@carbon/react';
|
|
16
14
|
import {
|
|
17
15
|
Phone,
|
|
@@ -21,8 +19,6 @@ import {
|
|
|
21
19
|
RuleLocked,
|
|
22
20
|
Renew,
|
|
23
21
|
ChevronRight,
|
|
24
|
-
WarningAlt,
|
|
25
|
-
CheckmarkFilled,
|
|
26
22
|
ArrowLeft,
|
|
27
23
|
Time,
|
|
28
24
|
} from '@carbon/react/icons';
|
|
@@ -83,6 +79,7 @@ type OTPVerificationModalProps = {
|
|
|
83
79
|
|
|
84
80
|
authMode?: 'otp-only' | 'multi';
|
|
85
81
|
whitelistedForOTP?: boolean;
|
|
82
|
+
facilityBiometricsEnforced?: boolean;
|
|
86
83
|
onStartBiometric?: () => Promise<{
|
|
87
84
|
embed_url: string;
|
|
88
85
|
authorization_code: string;
|
|
@@ -120,6 +117,7 @@ type OTPVerificationModalProps = {
|
|
|
120
117
|
|
|
121
118
|
onRejected?: () => void;
|
|
122
119
|
ekycOrigin?: string;
|
|
120
|
+
visitAction?: 'start' | 'end';
|
|
123
121
|
};
|
|
124
122
|
|
|
125
123
|
const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
@@ -135,6 +133,7 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
135
133
|
onCleanup,
|
|
136
134
|
authMode = 'otp-only',
|
|
137
135
|
whitelistedForOTP = false,
|
|
136
|
+
facilityBiometricsEnforced = false,
|
|
138
137
|
onStartBiometric,
|
|
139
138
|
onBiometricSuccess,
|
|
140
139
|
onBiometricCancel,
|
|
@@ -146,8 +145,10 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
146
145
|
onRequestWhitelist,
|
|
147
146
|
onRejected,
|
|
148
147
|
ekycOrigin,
|
|
148
|
+
visitAction = 'start',
|
|
149
149
|
}) => {
|
|
150
150
|
const { t } = useTranslation();
|
|
151
|
+
const canUseOtp = !facilityBiometricsEnforced || whitelistedForOTP;
|
|
151
152
|
const [otp, setOtp] = useState('');
|
|
152
153
|
const [newPhoneNumber, setNewPhoneNumber] = useState(phoneNumber);
|
|
153
154
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -208,6 +209,35 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
208
209
|
}
|
|
209
210
|
}, []);
|
|
210
211
|
|
|
212
|
+
// Reset mode and critical state when authMode or context changes (e.g., when modal is reused for different flows)
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const initialMode = authMode === 'multi' ? 'auth-landing' : 'landing';
|
|
215
|
+
setMode(initialMode);
|
|
216
|
+
setError(null);
|
|
217
|
+
setOtp('');
|
|
218
|
+
setFailedVerifyCount(0);
|
|
219
|
+
|
|
220
|
+
// Reset biometric state
|
|
221
|
+
setBiometricEmbedUrl(null);
|
|
222
|
+
setBiometricLoading(false);
|
|
223
|
+
setBiometricRetrying(false);
|
|
224
|
+
setBiometricFinalizing(false);
|
|
225
|
+
setBiometricPollElapsedSec(0);
|
|
226
|
+
biometricResultRef.current = null;
|
|
227
|
+
|
|
228
|
+
// Reset phone number and whitelist states
|
|
229
|
+
setNewPhoneNumber(phoneNumber);
|
|
230
|
+
setCurrentPhoneNumber(phoneNumber);
|
|
231
|
+
setWhitelistReasonCode('');
|
|
232
|
+
setWhitelistReasonText('');
|
|
233
|
+
setWhitelistBiometricAttempts(0);
|
|
234
|
+
setWhitelistAttachment(null);
|
|
235
|
+
setWhitelistSubmitting(false);
|
|
236
|
+
|
|
237
|
+
// Stop any active polling
|
|
238
|
+
stopBiometricPolling();
|
|
239
|
+
}, [authMode, phoneNumber, patientCRId, stopBiometricPolling]);
|
|
240
|
+
|
|
211
241
|
useEffect(() => {
|
|
212
242
|
return () => {
|
|
213
243
|
onCleanup?.();
|
|
@@ -275,7 +305,6 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
275
305
|
try {
|
|
276
306
|
setRequestingOtp(true);
|
|
277
307
|
await onRequestOtp?.(phone);
|
|
278
|
-
|
|
279
308
|
setOtp('');
|
|
280
309
|
setCurrentPhoneNumber(phone);
|
|
281
310
|
setMode('verify-otp');
|
|
@@ -641,7 +670,7 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
641
670
|
};
|
|
642
671
|
|
|
643
672
|
const handleEscapeHatchFromVerifyOtp = () => {
|
|
644
|
-
if (
|
|
673
|
+
if (canUseOtp) {
|
|
645
674
|
setOtp('');
|
|
646
675
|
setIsCountdownActive(false);
|
|
647
676
|
setMode('auth-landing');
|
|
@@ -730,7 +759,6 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
730
759
|
review_type: r.review_type,
|
|
731
760
|
requires_attachments: r.requires_attachments,
|
|
732
761
|
}));
|
|
733
|
-
|
|
734
762
|
return (
|
|
735
763
|
<React.Fragment>
|
|
736
764
|
<ModalHeader className={styles.sectionHeader} closeModal={handleClose}>
|
|
@@ -782,16 +810,16 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
782
810
|
</button>
|
|
783
811
|
|
|
784
812
|
<button
|
|
785
|
-
className={`${styles.methodCard} ${!
|
|
813
|
+
className={`${styles.methodCard} ${!canUseOtp ? styles.methodCardDisabled : ''}`}
|
|
786
814
|
onClick={handleSwitchToOtpFromAuthLanding}
|
|
787
|
-
disabled={!
|
|
815
|
+
disabled={!canUseOtp}>
|
|
788
816
|
<div className={`${styles.methodIconWrap} ${styles.methodIconWarning}`}>
|
|
789
817
|
<ChatBot size={22} />
|
|
790
818
|
</div>
|
|
791
819
|
<div className={styles.methodContent}>
|
|
792
820
|
<div className={styles.methodTitleRow}>
|
|
793
821
|
<p className={styles.methodTitle}>{t('btnUseOtp', 'Use OTP')}</p>
|
|
794
|
-
{!
|
|
822
|
+
{!canUseOtp && (
|
|
795
823
|
<span className={styles.badgeRequired}>{t('whitelistingRequired', 'Whitelisting required')}</span>
|
|
796
824
|
)}
|
|
797
825
|
</div>
|
|
@@ -802,10 +830,10 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
802
830
|
)}
|
|
803
831
|
</p>
|
|
804
832
|
</div>
|
|
805
|
-
{
|
|
833
|
+
{canUseOtp && <ChevronRight size={18} className={styles.methodArrow} />}
|
|
806
834
|
</button>
|
|
807
835
|
|
|
808
|
-
{!
|
|
836
|
+
{!canUseOtp && (
|
|
809
837
|
<>
|
|
810
838
|
<div className={styles.dividerWithText}>{t('or', 'or')}</div>
|
|
811
839
|
<button
|
|
@@ -898,10 +926,12 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
898
926
|
'otpFailedWhitelistedSubtitle',
|
|
899
927
|
'Try biometrics instead — biometric capture is available for whitelisted patients.',
|
|
900
928
|
)
|
|
901
|
-
:
|
|
929
|
+
: facilityBiometricsEnforced
|
|
930
|
+
? t(
|
|
902
931
|
'otpFailedNotWhitelistedSubtitle',
|
|
903
932
|
'Patient may not be receiving the SMS. Submit a whitelist request to bypass OTP for future visits.',
|
|
904
933
|
)
|
|
934
|
+
: t('otpFailedRetrySubtitle', 'OTP failed multiple times. Please try again or use biometrics.')
|
|
905
935
|
}
|
|
906
936
|
/>
|
|
907
937
|
<Button
|
|
@@ -915,8 +945,10 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
915
945
|
<InlineLoading description={t('checkingExistingRequest', 'Checking existing requests…')} />
|
|
916
946
|
) : whitelistedForOTP ? (
|
|
917
947
|
t('switchToBiometric', 'Switch to biometric')
|
|
918
|
-
) : (
|
|
948
|
+
) : facilityBiometricsEnforced ? (
|
|
919
949
|
t('btnRequestOtpWhitelisting', 'Request OTP Whitelisting')
|
|
950
|
+
) : (
|
|
951
|
+
t('switchToBiometric', 'Switch to biometric')
|
|
920
952
|
)}
|
|
921
953
|
</Button>
|
|
922
954
|
</div>
|
|
@@ -940,7 +972,9 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
940
972
|
<InlineLoading
|
|
941
973
|
description={
|
|
942
974
|
biometricFinalizing
|
|
943
|
-
?
|
|
975
|
+
? visitAction === 'end'
|
|
976
|
+
? t('biometricEndingVisit', 'Verified ending visit…')
|
|
977
|
+
: t('biometricCreatingVisit', 'Verified creating visit…')
|
|
944
978
|
: t('biometricWaitingForApproval', 'Waiting for SHA to confirm capture…')
|
|
945
979
|
}
|
|
946
980
|
status="active"
|
|
@@ -961,7 +995,7 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
961
995
|
kind="error"
|
|
962
996
|
title={t('secureAuthBiometricFailedTitle', 'Biometric Verification Failed')}
|
|
963
997
|
subtitle={
|
|
964
|
-
|
|
998
|
+
canUseOtp
|
|
965
999
|
? t(
|
|
966
1000
|
'secureAuthBiometricFailedBodyWhitelisted',
|
|
967
1001
|
'Please try again or use OTP verification to continue.',
|
|
@@ -975,7 +1009,7 @@ const OTPVerificationModal: FC<OTPVerificationModalProps> = ({
|
|
|
975
1009
|
|
|
976
1010
|
<p className={styles.chooseLabel}>{t('secureAuthChooseReason', 'Choose an option')}</p>
|
|
977
1011
|
|
|
978
|
-
{
|
|
1012
|
+
{canUseOtp ? (
|
|
979
1013
|
<button className={styles.methodCard} onClick={handleSwitchToOtpFromAuthLanding}>
|
|
980
1014
|
<div className={`${styles.methodIconWrap} ${styles.methodIconWarning}`}>
|
|
981
1015
|
<ChatBot size={22} />
|
package/src/shared/patient-chart/patient-summary-dashboard/patient-summary-dashboard.component.tsx
CHANGED
|
@@ -1,80 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
Activity,
|
|
4
|
-
Attachment,
|
|
5
|
-
Calendar,
|
|
6
|
-
CloudMonitoring,
|
|
7
|
-
Dashboard,
|
|
8
|
-
IbmWatsonDiscovery,
|
|
9
|
-
Settings,
|
|
10
|
-
} from '@carbon/react/icons';
|
|
11
|
-
import React, { useMemo } from 'react';
|
|
12
|
-
import { useTranslation } from 'react-i18next';
|
|
13
|
-
import ExtensionTabs, { ExtensionTabItem } from '../../tabs/extension-tabs.component';
|
|
14
|
-
import styles from './patient-summary-dashboard.scss';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ExtensionTabs from '../../tabs/extension-tabs.component';
|
|
15
3
|
|
|
16
4
|
type PatientSummaryDashboardProps = {
|
|
17
5
|
patientUuid: string;
|
|
18
|
-
patient: fhir.Patient;
|
|
19
6
|
};
|
|
20
|
-
|
|
21
|
-
const PatientSummaryDashboard: React.FC<PatientSummaryDashboardProps> = ({ patientUuid
|
|
22
|
-
|
|
23
|
-
const state = useMemo(() => ({ patientUuid, patient }), [patientUuid, patient]);
|
|
24
|
-
const items: Array<ExtensionTabItem> = [
|
|
25
|
-
{
|
|
26
|
-
label: t('patientSummary', 'Patient Summary'),
|
|
27
|
-
icon: Dashboard,
|
|
28
|
-
slotName: 'ewf-patient-summary-slot',
|
|
29
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
label: t('vitalsAndAnthropometric', 'Vitals & Anthropometric'),
|
|
33
|
-
icon: Activity,
|
|
34
|
-
slotName: 'ewf-vitals-slot',
|
|
35
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
label: t('carePanel', 'Care Panel'),
|
|
39
|
-
icon: CloudMonitoring,
|
|
40
|
-
slotName: 'ewf-care-panel-slot',
|
|
41
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
label: t('immunizations', 'Immunizations'),
|
|
45
|
-
icon: IbmWatsonDiscovery,
|
|
46
|
-
slotName: 'ewf-immunizations-slot',
|
|
47
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
label: t('relationships', 'Relationships'),
|
|
51
|
-
icon: Settings,
|
|
52
|
-
slotName: 'ewf-relationships-slot',
|
|
53
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
label: t('appointments', 'Appointments'),
|
|
57
|
-
icon: Calendar,
|
|
58
|
-
slotName: 'ewf-appointments-slot',
|
|
59
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
label: t('attachments', 'Attachments'),
|
|
63
|
-
icon: Attachment,
|
|
64
|
-
slotName: 'ewf-attachments-slot',
|
|
65
|
-
slotClassName: styles.ewfExtensionSlot,
|
|
66
|
-
},
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<Layer>
|
|
71
|
-
<Grid condensed>
|
|
72
|
-
<Column lg={16} md={8} sm={4}>
|
|
73
|
-
<ExtensionTabs items={items} state={state} />
|
|
74
|
-
</Column>
|
|
75
|
-
</Grid>
|
|
76
|
-
</Layer>
|
|
77
|
-
);
|
|
7
|
+
const PATIENT_CHART_PATIENT_SUMMARY_EXTENSION_SLOT = 'ewf-patient-chart-patient-summary-tabs-extension-slot';
|
|
8
|
+
const PatientSummaryDashboard: React.FC<PatientSummaryDashboardProps> = ({ patientUuid }) => {
|
|
9
|
+
return <ExtensionTabs extensionSlotName={PATIENT_CHART_PATIENT_SUMMARY_EXTENSION_SLOT} patientUuid={patientUuid} />;
|
|
78
10
|
};
|
|
79
11
|
|
|
80
12
|
export default PatientSummaryDashboard;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ExtensionSlot, FHIRResource } from '@openmrs/esm-framework';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styles from './patient-summary-dashboard.scss';
|
|
4
|
+
export const createTabExtension = (extensionSlotName?: string) => {
|
|
5
|
+
return function TabExtension(props: any) {
|
|
6
|
+
return <TabWithExtensionSlot extensionSlotName={extensionSlotName} {...props} />;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const TabWithExtensionSlot: React.FC<{
|
|
11
|
+
extensionSlotName?: string;
|
|
12
|
+
patientUuid: string;
|
|
13
|
+
patient: FHIRResource;
|
|
14
|
+
_meta?: {
|
|
15
|
+
slot?: string;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
};
|
|
18
|
+
}> = ({ extensionSlotName, patientUuid, patient, _meta }) => {
|
|
19
|
+
if (!_meta?.slot && !extensionSlotName) {
|
|
20
|
+
console.warn(
|
|
21
|
+
'No slot provided for TabExtension. Please provide a slot in the route meta or as an argument to createTabExtension.',
|
|
22
|
+
);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const { slot, ...others } = _meta || {};
|
|
26
|
+
return (
|
|
27
|
+
<ExtensionSlot
|
|
28
|
+
name={_meta?.slot ?? (extensionSlotName as string)}
|
|
29
|
+
state={{ patientUuid, patient, ...others }}
|
|
30
|
+
className={styles.ewfExtensionSlot}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -1,41 +1,59 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
label: React.ReactNode;
|
|
7
|
-
icon?: React.ComponentType<unknown>;
|
|
8
|
-
slotName: string;
|
|
9
|
-
slotClassName?: string;
|
|
10
|
-
};
|
|
1
|
+
import { Layer, Tab, TabList, TabPanel, TabPanels, Tabs, TabsSkeleton } from '@carbon/react';
|
|
2
|
+
import { ErrorState, Extension, translateFrom, usePatient } from '@openmrs/esm-framework';
|
|
3
|
+
import { ComponentContext, useExtensionSlot } from '@openmrs/esm-framework/src/internal';
|
|
4
|
+
import React, { useMemo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
11
6
|
|
|
12
7
|
type ExtensionTabsProps = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
contained?: boolean;
|
|
8
|
+
extensionSlotName: string;
|
|
9
|
+
patientUuid: string;
|
|
16
10
|
};
|
|
17
11
|
|
|
18
|
-
const ExtensionTabs: React.FC<ExtensionTabsProps> = ({
|
|
19
|
-
const
|
|
12
|
+
const ExtensionTabs: React.FC<ExtensionTabsProps> = ({ extensionSlotName, patientUuid }) => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const { patient, isLoading, error } = usePatient(patientUuid);
|
|
15
|
+
const state = useMemo(() => ({ patientUuid, patient }), [patientUuid, patient]);
|
|
16
|
+
|
|
17
|
+
const { extensions, extensionSlotModuleName } = useExtensionSlot(extensionSlotName, state);
|
|
18
|
+
if (isLoading) {
|
|
19
|
+
return <TabsSkeleton />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (error) {
|
|
23
|
+
return <ErrorState headerTitle={t('error', 'Error')} error={error} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
return (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
<Layer>
|
|
28
|
+
<Tabs>
|
|
29
|
+
<TabList contained>
|
|
30
|
+
{extensions.map((ext) => (
|
|
31
|
+
<Tab key={ext.name}>{translateFrom(extensionSlotModuleName, ext.meta.title, ext.meta.title)}</Tab>
|
|
32
|
+
))}
|
|
33
|
+
</TabList>
|
|
34
|
+
<TabPanels
|
|
35
|
+
data-extension-slot-name={extensionSlotName}
|
|
36
|
+
data-extension-slot-module-name={extensionSlotModuleName}>
|
|
37
|
+
{extensions.map((ext) => (
|
|
38
|
+
<TabPanel key={ext.name}>
|
|
39
|
+
<ComponentContext.Provider
|
|
40
|
+
key={ext.id}
|
|
41
|
+
value={{
|
|
42
|
+
featureName: ext.meta.featureName,
|
|
43
|
+
moduleName: ext.moduleName,
|
|
44
|
+
extension: {
|
|
45
|
+
extensionId: ext.id,
|
|
46
|
+
extensionSlotName: extensionSlotName,
|
|
47
|
+
extensionSlotModuleName: ext.moduleName,
|
|
48
|
+
},
|
|
49
|
+
}}>
|
|
50
|
+
<Extension state={state} />
|
|
51
|
+
</ComponentContext.Provider>
|
|
52
|
+
</TabPanel>
|
|
53
|
+
))}
|
|
54
|
+
</TabPanels>
|
|
55
|
+
</Tabs>
|
|
56
|
+
</Layer>
|
|
39
57
|
);
|
|
40
58
|
};
|
|
41
59
|
|
|
@@ -187,7 +187,7 @@ export interface SavannahErrorResponse {
|
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
189
|
* Returns the most specific human-readable error message available.
|
|
190
|
-
* Priority: upstream_error.message > upstream_error.error >
|
|
190
|
+
* Priority: upstream_error.message > upstream_error.error > message > error > fallback
|
|
191
191
|
*/
|
|
192
192
|
export function extractUpstreamError(
|
|
193
193
|
response: SavannahErrorResponse | string | unknown,
|
|
@@ -213,16 +213,16 @@ export function extractUpstreamError(
|
|
|
213
213
|
return upstreamErr;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
const topError = res.error?.trim();
|
|
217
|
-
if (topError && !res.upstream_error) {
|
|
218
|
-
return topError;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
216
|
const topMsg = res.message?.trim();
|
|
222
217
|
if (topMsg) {
|
|
223
218
|
return topMsg;
|
|
224
219
|
}
|
|
225
220
|
|
|
221
|
+
const topError = res.error?.trim();
|
|
222
|
+
if (topError && !res.upstream_error) {
|
|
223
|
+
return topError;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
226
|
if (topError) {
|
|
227
227
|
return topError;
|
|
228
228
|
}
|
package/translations/am.json
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"accounting": "Accounting",
|
|
3
3
|
"actions": "እርምጃዎች",
|
|
4
4
|
"activeMedications": "አሁን ያሉ መድኃኒቶች",
|
|
5
|
-
"activeMedicationsCount": "Active Medications",
|
|
6
5
|
"activeVisitExists": "This patient already has an active visit",
|
|
7
6
|
"add": "Add",
|
|
8
7
|
"addAttachment": "Add attachment",
|
|
@@ -10,7 +9,6 @@
|
|
|
10
9
|
"additionalVisitInformationUpdatedSuccessfully": "Additional visit information updated successfully",
|
|
11
10
|
"addMedicationOrder": "Add Medication Order",
|
|
12
11
|
"addOrder": "Add Order",
|
|
13
|
-
"admissionRequest": "Admission request",
|
|
14
12
|
"admissions": "ተኝተዋል",
|
|
15
13
|
"Admitted/OPD": "የተኝተው/የውጪ ሕመምተኛ ጉብኝቶች",
|
|
16
14
|
"all": "All",
|
|
@@ -35,13 +33,16 @@
|
|
|
35
33
|
"awaitingConsultation": "Awaiting consultation",
|
|
36
34
|
"awaitingProcedure": "Awaiting Procedure",
|
|
37
35
|
"back": "Back",
|
|
36
|
+
"biometricAgentNotReachable": "Biometric agent not running. Please start the agent on this workstation and try again.",
|
|
38
37
|
"biometricAttemptsHelper": "How many times biometric capture has been attempted (use 0 if not applicable).",
|
|
39
38
|
"biometricAttemptsLabel": "Biometric attempts",
|
|
40
39
|
"biometricCapture": "Biometric capture",
|
|
41
40
|
"biometricCaptureTitle": "Biometric capture",
|
|
42
41
|
"biometricCreatingVisit": "Verified creating visit…",
|
|
42
|
+
"biometricEndingVisit": "Verified ending visit…",
|
|
43
43
|
"biometricError": "Biometric error",
|
|
44
44
|
"biometricHint": "Follow the on-screen instructions in the capture window.",
|
|
45
|
+
"biometricMissingNationalId": "Provider National ID not configured. Please add it to your provider profile.",
|
|
45
46
|
"biometricNoEmbedUrl": "Biometric session did not return an embed URL.",
|
|
46
47
|
"biometricRejected": "Biometric verification was rejected. You can try again or use OTP.",
|
|
47
48
|
"biometricRetrying": "Restarting…",
|
|
@@ -71,19 +72,22 @@
|
|
|
71
72
|
"checkStatusNow": "Check status now",
|
|
72
73
|
"chooseAnOption": "Choose an option",
|
|
73
74
|
"chooseIdentifierType": "Choose identifier type",
|
|
74
|
-
"
|
|
75
|
+
"claimProcessingError": "Error",
|
|
75
76
|
"clearAll": "Clear All",
|
|
76
77
|
"clearFilters": "Clear filters",
|
|
77
78
|
"clientRegistry": "Client registry verification",
|
|
78
79
|
"clientsPatientsAttendedTo": "Clients/Patients attended to",
|
|
79
80
|
"clientsPatientsWaiting": "Clients/Patients waiting",
|
|
80
81
|
"clinicalEncounter": "Clinical Encounter",
|
|
82
|
+
"clinicalEncounterTab": "የክሊኒካል ግኝት",
|
|
83
|
+
"clinicalNotes": "የክሊኒካል ማስታወሻዎች",
|
|
81
84
|
"comingFrom": "Coming from",
|
|
82
85
|
"completedProcedures": "Completed Procedures",
|
|
83
86
|
"concentrationRate": "Concentration rate",
|
|
84
87
|
"configureQueueRooms": "",
|
|
85
88
|
"confirmationTxt": "Verify the phone number before OTP",
|
|
86
89
|
"consultation": "Consultation",
|
|
90
|
+
"continue": "Continue",
|
|
87
91
|
"continueWithCare": "Continue with care",
|
|
88
92
|
"CRNumber": "CR Number",
|
|
89
93
|
"customCheckin": "Check in",
|
|
@@ -101,9 +105,13 @@
|
|
|
101
105
|
"diagnosisRequired": "Diagnosis is required",
|
|
102
106
|
"diastolicBP": "BP Diastolic",
|
|
103
107
|
"discard": "Discard",
|
|
108
|
+
"dischargeReason": "Discharge reason",
|
|
109
|
+
"dischargeReasonIntro": "Select the reason for ending this visit. You will then be asked to verify your identity.",
|
|
104
110
|
"diseases": "በሽታዎች",
|
|
105
|
-
"
|
|
106
|
-
"
|
|
111
|
+
"drugOrderSaved": "Drug order saved",
|
|
112
|
+
"drugOrderSavedToBackend": "Drug order has been saved to backend",
|
|
113
|
+
"drugOrderSaveFailed": "Failed to save drug order",
|
|
114
|
+
"drugsIVFluids": "Drugs and IV Fluids Given",
|
|
107
115
|
"editVisit": "Edit visit",
|
|
108
116
|
"effect": "Effect",
|
|
109
117
|
"emergency": "Emergency",
|
|
@@ -112,7 +120,9 @@
|
|
|
112
120
|
"Encounter deleted successfully": "",
|
|
113
121
|
"encounterDate": "Encounter Date",
|
|
114
122
|
"encounterDetails": "Encounter Details",
|
|
123
|
+
"endActiveVisitConfirmation": "Are you sure you want to end this active visit?",
|
|
115
124
|
"endActiveVisitFirst": "You must end the current visit before starting a new one.",
|
|
125
|
+
"endCustomActiveVisit": "End Active Visit",
|
|
116
126
|
"endDate": "End date",
|
|
117
127
|
"endDateTimeRequired": "End date and time are required",
|
|
118
128
|
"ended": "Ended",
|
|
@@ -120,6 +130,8 @@
|
|
|
120
130
|
"endTimeFormat": "End time format",
|
|
121
131
|
"endTimeMustBeAfterMostRecentEncounter": "End time must be on or after {{lastEncounterDatetime}}",
|
|
122
132
|
"endTimeMustBeAfterStartTime": "End time must be after start time",
|
|
133
|
+
"endVisit_title": "End Visit",
|
|
134
|
+
"endVisitTitle": "End Visit",
|
|
123
135
|
"enterConcentrationRate": "Enter 1, 2.3, 3%, 4.5%",
|
|
124
136
|
"enterFluidsGiven": "Enter fluids given",
|
|
125
137
|
"enterIdentifierNumber": "Enter identifier number",
|
|
@@ -140,16 +152,13 @@
|
|
|
140
152
|
"errorLoadingQueues": "Error loading queues",
|
|
141
153
|
"errorUpdatingVisitAttribute": "Error updating the {{attributeName}} visit attribute",
|
|
142
154
|
"errorUpdatingVisitDetails": "Error updating visit details",
|
|
143
|
-
"failedToLoadDrugOrders": "Failed to load drug orders",
|
|
144
|
-
"fieldRequired": "This field is required",
|
|
145
|
-
"filters": "Filters",
|
|
146
|
-
"fluidsAndParenteralNutritionSection": "የፈሳሾች እና የፓሬንቴራል አመጋገብ ክፍል",
|
|
147
155
|
"estimatedBloodLoss": "Estimated Blood Loss (ML)",
|
|
148
156
|
"estimatedBloodLossInvalid": "Estimated blood loss must be 0 or greater",
|
|
149
157
|
"estimatedBloodLossRequired": "Estimated blood loss is required",
|
|
150
158
|
"etco2": "EtCO2",
|
|
151
159
|
"Failed to load membrane amniotic fluid data": "",
|
|
152
160
|
"Failed to save membrane amniotic fluid data": "",
|
|
161
|
+
"failedToDeleteEncounter": "{{status}} {{statusText}}",
|
|
153
162
|
"failedToSaveAnaestheticRecord": "Failed to save anaesthetic record",
|
|
154
163
|
"failedToSaveDrugsIvFluids": "Failed to save drugs and IV fluids data",
|
|
155
164
|
"failedToSavePartographyData": "Failed to save partography data: {{error}}",
|
|
@@ -176,20 +185,11 @@
|
|
|
176
185
|
"immunizations": "Immunizations",
|
|
177
186
|
"inductionAirway": "Induction (Airway)",
|
|
178
187
|
"inPatient": "In-Patient",
|
|
179
|
-
"
|
|
188
|
+
"insuranceClaimProcessingFailed": "Insurance claim processing failed",
|
|
180
189
|
"interoperativeRecordDrugGiven": "Interoperative record",
|
|
181
190
|
"inThePast": "In the past",
|
|
182
191
|
"investigationAwaiting": "Investigation Awaiting",
|
|
183
192
|
"investigationCompleted": "Investigation Completed",
|
|
184
|
-
"administrationVolume": "መጠን",
|
|
185
|
-
"administrationFrequency": "ድግግሞሽ",
|
|
186
|
-
"administrationRate": "ፍጥነት",
|
|
187
|
-
"administrationDuration": "ቆይታ",
|
|
188
|
-
"administrationVolumeValue": "መጠን: {{value}}",
|
|
189
|
-
"administrationFrequencyValue": "ድግግሞሽ: {{value}}",
|
|
190
|
-
"administrationRateValue": "ፍጥነት: {{value}}",
|
|
191
|
-
"administrationDurationValue": "ቆይታ: {{value}}",
|
|
192
|
-
"itemAdministrationInstructions": "Item & Administration Instructions (Volume, Frequency, Rate, Duration)",
|
|
193
193
|
"lab": "Lab",
|
|
194
194
|
"labAwaiting": "Lab Awaiting",
|
|
195
195
|
"labCompleted": "Lab Completed",
|
|
@@ -221,6 +221,7 @@
|
|
|
221
221
|
"newPatientResults_other": "New patient(s) found ({{count}})",
|
|
222
222
|
"nextPage": "Next page",
|
|
223
223
|
"noAnaestheticRecordData": "No anaesthetic record data available",
|
|
224
|
+
"noCRNumber": "Patient has no SHA CR number.",
|
|
224
225
|
"noDependentsFound": "No dependents found for this patient",
|
|
225
226
|
"noEncounterTypeFound": "No encounter type found for graph: {{graphType}}",
|
|
226
227
|
"noFilters": "No filters",
|
|
@@ -244,7 +245,6 @@
|
|
|
244
245
|
"noValidObservations": "No valid observations to save",
|
|
245
246
|
"noVisitTypesToDisplay": "No visit types to display",
|
|
246
247
|
"numberofEmergencyCasesSeen": "የተታዩ የድንገተኛ ሁኔታ ሕመምተኞች ቁጥር",
|
|
247
|
-
"nursingTreatmentSheet": "Nursing Treatment Sheet",
|
|
248
248
|
"offlineVisit": "Offline Visit",
|
|
249
249
|
"ongoing": "Ongoing",
|
|
250
250
|
"ongoingProcedures": "Ongoing Procedures",
|
|
@@ -263,6 +263,7 @@
|
|
|
263
263
|
"otpExpiryInfo": "The OTP will be valid for {{minutes}} minutes after it is sent.",
|
|
264
264
|
"otpFailedNotWhitelistedSubtitle": "Patient may not be receiving the SMS. Submit a whitelist request to bypass OTP for future visits.",
|
|
265
265
|
"otpFailedRepeatedly": "OTP failed 3 times",
|
|
266
|
+
"otpFailedRetrySubtitle": "OTP failed multiple times. Please try again or use biometrics.",
|
|
266
267
|
"otpFailedWhitelisted": "OTP not working?",
|
|
267
268
|
"otpFailedWhitelistedSubtitle": "Try biometrics instead — biometric capture is available for whitelisted patients.",
|
|
268
269
|
"otpRequestError": "Error requesting OTP",
|
|
@@ -272,15 +273,16 @@
|
|
|
272
273
|
"otpVerificationError": "Error verifying OTP",
|
|
273
274
|
"otpVerificationFailed": "OTP verification failed. Please try again.",
|
|
274
275
|
"partOfFormDidntLoad": "Part of the form did not load",
|
|
276
|
+
"partographyDataSavedSuccessfully": "Partography data saved successfully",
|
|
275
277
|
"pastMedications": "ያለፉ መድኃኒቶች",
|
|
276
|
-
"pastMedicationsCount": "Past Medications",
|
|
277
278
|
"patientAttended": "ሕመምተኛ ተንከባክቧል",
|
|
278
279
|
"patientCategory": "የሕመምተኛ ምድብ",
|
|
279
280
|
"patientExamination": "የሕመምተኛ ምርመራ",
|
|
280
281
|
"patientHistory": "የሕመምተኛ ታሪክ",
|
|
281
282
|
"patientManagement": "የሕመምተኛ አስተዳደር",
|
|
283
|
+
"patientMissingUuid": "Patient is missing a UUID",
|
|
282
284
|
"patientsAwaiting": "የሚጠብቅ ሕመምተኛ",
|
|
283
|
-
"
|
|
285
|
+
"patientSummaryTab": "የታካሚ ማጠቃለያ",
|
|
284
286
|
"pendingMedicationOrders": "Pending Medication Orders",
|
|
285
287
|
"pharmacy": "Pharmacy",
|
|
286
288
|
"pharmacyOrdersDescription": "Active medication prescriptions awaiting pharmacist review and dispensing",
|
|
@@ -298,6 +300,7 @@
|
|
|
298
300
|
"priority": "Priority",
|
|
299
301
|
"priorityComment": "Priority Comment",
|
|
300
302
|
"procedures": "Procedures",
|
|
303
|
+
"proceduresAndOrders": "ሂደቶች እና ትዕዛዞች",
|
|
301
304
|
"proceduresAwaiting": "Procedures Awaiting",
|
|
302
305
|
"proceduresCompleted": "Procedures Completed",
|
|
303
306
|
"proceduresOrders": "Procedures Orders",
|
|
@@ -349,7 +352,6 @@
|
|
|
349
352
|
"searchForAPatient": "Search for a patient",
|
|
350
353
|
"searchForAVisitType": "Search for a visit type",
|
|
351
354
|
"searching": "Searching...",
|
|
352
|
-
"searchingDiagnosis": "Searching diagnosis...",
|
|
353
355
|
"searchMedicationOrders": "Search medication orders",
|
|
354
356
|
"searchPatients": "Search for Patient(s)",
|
|
355
357
|
"searchPlaceholder": "Type to search (minimum 3 characters)...",
|
|
@@ -367,6 +369,7 @@
|
|
|
367
369
|
"secureAuthOtpOption": "One-Time Password (Whitelisting is a must) sent to your registered phone number or email",
|
|
368
370
|
"secureAuthTitle": "Secure Patient Authentication",
|
|
369
371
|
"selectAnOption": "Select an option",
|
|
372
|
+
"selectDischargeReason": "Select a reason",
|
|
370
373
|
"selectIdentifierAndNumber": "Please select an identifier type and enter an identifier number",
|
|
371
374
|
"selectLocation": "Select a location",
|
|
372
375
|
"selectOrSearchDiagnosis": "Select from list or type at least 3 letters to search ICD-11 diagnosis",
|
|
@@ -401,6 +404,9 @@
|
|
|
401
404
|
"tableView": "Table View",
|
|
402
405
|
"theVisitIs": "The visit is",
|
|
403
406
|
"time": "Time",
|
|
407
|
+
"Time": "",
|
|
408
|
+
"timeGiven": "Time given",
|
|
409
|
+
"timeGivenRequired": "Time given is required",
|
|
404
410
|
"top10DiseasesOver5": "ከ5 ዓመት በላይ ዕድሜ ያላቸው ከፍተኛ 10 በሽታዎች",
|
|
405
411
|
"top10DiseasesUnder5": "ከ5 ዓመት በታች ዕድሜ ያላቸው ከፍተኛ 10 በሽታዎች",
|
|
406
412
|
"totalNumberofReferrals-IN": "አጠቃላይ የመላኪያ ቁጥር - ወደ ውስጥ",
|
|
@@ -437,6 +443,7 @@
|
|
|
437
443
|
"visitDetails": "Visit Details",
|
|
438
444
|
"visitDetailsUpdated": "Visit details updated",
|
|
439
445
|
"visitDetailsUpdatedSuccessfully": "{{visit}} updated successfully",
|
|
446
|
+
"visitEnded": "Visit ended",
|
|
440
447
|
"visitLocation": "Visit Location",
|
|
441
448
|
"visitLocationRequired": "Visit location is required",
|
|
442
449
|
"visitProvider": "Visit Provider",
|
|
@@ -480,5 +487,6 @@
|
|
|
480
487
|
"whitelistStillPendingShort": "Request is still pending review. We will keep checking automatically.",
|
|
481
488
|
"whitelistSubmitFailed": "Whitelist submission failed.",
|
|
482
489
|
"whitelistWaitingTitle": "Waiting for whitelist approval",
|
|
483
|
-
"yearsOld": "years old"
|
|
490
|
+
"yearsOld": "years old",
|
|
491
|
+
"youWillProcessTheClaim": "You will process the insurance claim for this visit."
|
|
484
492
|
}
|