@kenyaemr/esm-morgue-app 5.4.2-pre.2297 → 5.4.2-pre.2301
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 +10 -10
- package/dist/201.js +1 -1
- package/dist/201.js.map +1 -1
- package/dist/398.js +1 -1
- package/dist/398.js.map +1 -1
- package/dist/{826.js → 579.js} +2 -2
- package/dist/{826.js.map → 579.js.map} +1 -1
- package/dist/632.js +1 -1
- package/dist/632.js.map +1 -1
- package/dist/633.js +1 -1
- package/dist/633.js.map +1 -1
- package/dist/{146.js → 989.js} +2 -2
- package/dist/989.js.map +1 -0
- package/dist/kenyaemr-esm-morgue-app.js +1 -1
- package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +73 -73
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/bed-layout/discharged/discharged-bed-layout.component.tsx +32 -3
- package/src/bed-layout/discharged/discharged-bed-layout.resource.ts +22 -12
- package/src/bed-linelist-view/admitted/admitted-bed-linelist-view.component.tsx +1 -0
- package/src/bed-linelist-view/discharged/discharged-bed-line-view.component.tsx +0 -1
- package/src/config-schema.ts +0 -6
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.resource.ts +117 -75
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.scss +1 -1
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.workspace.tsx +15 -14
- package/src/forms/discharge-deceased-person-workspace/discharge-body.resource.ts +124 -26
- package/src/forms/discharge-deceased-person-workspace/discharge-body.workspace.tsx +188 -82
- package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.workspace.tsx +133 -48
- package/src/home/home.component.tsx +7 -2
- package/src/home/home.resource.ts +1 -1
- package/src/switcher/content-switcher.component.tsx +0 -1
- package/src/types/index.ts +1 -4
- package/src/utils/utils.ts +23 -39
- package/dist/146.js.map +0 -1
- /package/dist/{826.js.LICENSE.txt → 579.js.LICENSE.txt} +0 -0
- /package/dist/{146.js.LICENSE.txt → 989.js.LICENSE.txt} +0 -0
|
@@ -45,16 +45,24 @@ export const usePersonAttributes = (personUuid: string) => {
|
|
|
45
45
|
) => {
|
|
46
46
|
const { attributeType, value } = payload;
|
|
47
47
|
|
|
48
|
-
const existingAttribute = person.attributes.find((attr) => attr.uuid === attributeType);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{ attributeType
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
48
|
+
const existingAttribute = person.attributes.find((attr) => attr.attributeType?.uuid === attributeType);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
if (existingAttribute) {
|
|
52
|
+
return await updatePersonAttributes({ attributeType, value }, personUuid, existingAttribute.uuid);
|
|
53
|
+
} else {
|
|
54
|
+
return await createPersonAttribute({ attributeType, value }, personUuid);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error creating/updating person attribute:', error);
|
|
58
|
+
|
|
59
|
+
if (error?.responseBody?.error?.message?.includes('already in use')) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`The identifier "${value}" is already in use by another patient. Please use a different identifier.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw error;
|
|
58
66
|
}
|
|
59
67
|
};
|
|
60
68
|
|
|
@@ -67,74 +75,164 @@ export const usePersonAttributes = (personUuid: string) => {
|
|
|
67
75
|
createOrUpdatePersonAttribute,
|
|
68
76
|
};
|
|
69
77
|
};
|
|
70
|
-
|
|
71
78
|
export const useBills = (
|
|
72
79
|
patientUuid: string = '',
|
|
73
|
-
billStatus: PaymentStatus
|
|
80
|
+
billStatus: PaymentStatus | '' | string = '',
|
|
74
81
|
startingDate: Date = dayjs().startOf('day').toDate(),
|
|
75
82
|
endDate: Date = dayjs().endOf('day').toDate(),
|
|
76
83
|
) => {
|
|
77
84
|
const startingDateISO = startingDate.toISOString();
|
|
78
85
|
const endDateISO = endDate.toISOString();
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
let url = `${restBaseUrl}/cashier/bill?v=custom:(uuid,display,voided,voidReason,adjustedBy,cashPoint:(uuid,name),cashier:(uuid,display),dateCreated,lineItems,patient:(uuid,display),status,balance,totalPayments,totalExempted,totalDeposits,closed)&createdOnOrAfter=${startingDateISO}&createdOnOrBefore=${endDateISO}`;
|
|
88
|
+
|
|
89
|
+
if (patientUuid) {
|
|
90
|
+
url += `&patientUuid=${patientUuid}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (billStatus) {
|
|
94
|
+
url += `&status=${billStatus}`;
|
|
95
|
+
}
|
|
81
96
|
|
|
82
97
|
const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<PatientInvoice> } }>(
|
|
83
|
-
|
|
98
|
+
url,
|
|
84
99
|
openmrsFetch,
|
|
85
100
|
{
|
|
86
101
|
errorRetryCount: 2,
|
|
87
102
|
},
|
|
88
103
|
);
|
|
89
104
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
105
|
+
const results = data?.data?.results ?? [];
|
|
106
|
+
|
|
107
|
+
const sortedBills = sortBy(results, ['dateCreated']).reverse();
|
|
108
|
+
|
|
109
|
+
const patientFilteredBills = patientUuid
|
|
110
|
+
? sortedBills.filter((bill) => bill.patient?.uuid === patientUuid)
|
|
111
|
+
: sortedBills;
|
|
112
|
+
|
|
113
|
+
const statusFilteredBills = billStatus
|
|
114
|
+
? patientFilteredBills.filter((bill) => bill.status === billStatus)
|
|
115
|
+
: patientFilteredBills;
|
|
94
116
|
|
|
95
117
|
return {
|
|
96
|
-
bills:
|
|
118
|
+
bills: statusFilteredBills,
|
|
97
119
|
error,
|
|
98
120
|
isLoading,
|
|
99
121
|
isValidating,
|
|
100
122
|
mutate,
|
|
101
123
|
};
|
|
102
124
|
};
|
|
125
|
+
|
|
103
126
|
interface UseBlockDischargeWithPendingBillsProps {
|
|
104
127
|
patientUuid: string;
|
|
128
|
+
actionType: 'discharge' | 'dispose';
|
|
105
129
|
}
|
|
106
130
|
|
|
107
131
|
interface UseBlockDischargeWithPendingBillsReturn {
|
|
108
132
|
isDischargeBlocked: boolean;
|
|
109
|
-
pendingBills:
|
|
133
|
+
pendingBills: PatientInvoice[];
|
|
110
134
|
pendingBillsCount: number;
|
|
111
135
|
isLoadingBills: boolean;
|
|
112
136
|
billsError: any;
|
|
113
137
|
blockingMessage: string;
|
|
114
138
|
}
|
|
139
|
+
|
|
115
140
|
export const useBlockDischargeWithPendingBills = ({
|
|
116
141
|
patientUuid,
|
|
142
|
+
actionType,
|
|
117
143
|
}: UseBlockDischargeWithPendingBillsProps): UseBlockDischargeWithPendingBillsReturn => {
|
|
118
|
-
const { bills, isLoading, error } = useBills(patientUuid,
|
|
144
|
+
const { bills, isLoading, isValidating, error } = useBills(patientUuid, '');
|
|
119
145
|
|
|
120
146
|
const result = useMemo(() => {
|
|
121
|
-
|
|
147
|
+
if (isLoading || isValidating) {
|
|
148
|
+
return {
|
|
149
|
+
isDischargeBlocked: false,
|
|
150
|
+
pendingBills: [],
|
|
151
|
+
pendingBillsCount: 0,
|
|
152
|
+
isLoadingBills: true,
|
|
153
|
+
billsError: error,
|
|
154
|
+
blockingMessage: '',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (error) {
|
|
159
|
+
console.error('Error fetching bills:', error);
|
|
160
|
+
return {
|
|
161
|
+
isDischargeBlocked: false,
|
|
162
|
+
pendingBills: [],
|
|
163
|
+
pendingBillsCount: 0,
|
|
164
|
+
isLoadingBills: false,
|
|
165
|
+
billsError: error,
|
|
166
|
+
blockingMessage: '',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const pendingBills =
|
|
171
|
+
bills?.filter((bill) => {
|
|
172
|
+
const isPending = bill.status === PaymentStatus.PENDING;
|
|
173
|
+
const isNotVoided = !bill.voided;
|
|
174
|
+
|
|
175
|
+
let hasBalance = false;
|
|
176
|
+
if (bill.balance !== undefined) {
|
|
177
|
+
hasBalance = bill.balance > 0;
|
|
178
|
+
} else {
|
|
179
|
+
const totalAmount =
|
|
180
|
+
bill.lineItems?.reduce((sum, item) => {
|
|
181
|
+
return sum + (item as any).price * (item as any).quantity;
|
|
182
|
+
}, 0) || 0;
|
|
183
|
+
|
|
184
|
+
const totalPayments =
|
|
185
|
+
bill.totalPayments || bill.payments?.reduce((sum, payment) => sum + (payment.amount || 0), 0) || 0;
|
|
186
|
+
|
|
187
|
+
const totalExempted = bill.totalExempted || 0;
|
|
188
|
+
const totalDeposits = bill.totalDeposits || 0;
|
|
189
|
+
|
|
190
|
+
const outstandingBalance = totalAmount - totalPayments - totalExempted - totalDeposits;
|
|
191
|
+
hasBalance = outstandingBalance > 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const isNotClosed = bill.closed !== undefined ? !bill.closed : true;
|
|
195
|
+
|
|
196
|
+
return isPending && hasBalance && isNotVoided && isNotClosed;
|
|
197
|
+
}) || [];
|
|
198
|
+
|
|
122
199
|
const pendingBillsCount = pendingBills.length;
|
|
123
200
|
const isDischargeBlocked = pendingBillsCount > 0;
|
|
124
201
|
|
|
202
|
+
const totalOutstandingBalance = pendingBills.reduce((acc, bill) => {
|
|
203
|
+
if (bill.balance !== undefined) {
|
|
204
|
+
return acc + bill.balance;
|
|
205
|
+
} else {
|
|
206
|
+
const totalAmount =
|
|
207
|
+
bill.lineItems?.reduce((sum, item) => {
|
|
208
|
+
return sum + (item as any).price * (item as any).quantity;
|
|
209
|
+
}, 0) || 0;
|
|
210
|
+
|
|
211
|
+
const totalPayments =
|
|
212
|
+
bill.totalPayments || bill.payments?.reduce((sum, payment) => sum + (payment.amount || 0), 0) || 0;
|
|
213
|
+
|
|
214
|
+
const totalExempted = bill.totalExempted || 0;
|
|
215
|
+
const totalDeposits = bill.totalDeposits || 0;
|
|
216
|
+
|
|
217
|
+
const outstandingBalance = totalAmount - totalPayments - totalExempted - totalDeposits;
|
|
218
|
+
return acc + Math.max(0, outstandingBalance);
|
|
219
|
+
}
|
|
220
|
+
}, 0);
|
|
221
|
+
|
|
125
222
|
const blockingMessage = isDischargeBlocked
|
|
126
|
-
? `
|
|
127
|
-
|
|
223
|
+
? `The deceased patient cannot be ${actionType}d due to ${pendingBillsCount} unpaid ${
|
|
224
|
+
pendingBillsCount === 1 ? 'bill' : 'bills'
|
|
225
|
+
} (Total balance: Ksh.${totalOutstandingBalance.toLocaleString()}). Please visit the cashier to settle all outstanding bills before ${actionType}.`
|
|
128
226
|
: '';
|
|
129
227
|
return {
|
|
130
228
|
isDischargeBlocked,
|
|
131
229
|
pendingBills,
|
|
132
230
|
pendingBillsCount,
|
|
133
|
-
isLoadingBills:
|
|
231
|
+
isLoadingBills: false,
|
|
134
232
|
billsError: error,
|
|
135
233
|
blockingMessage,
|
|
136
234
|
};
|
|
137
|
-
}, [bills, isLoading, error]);
|
|
235
|
+
}, [bills, isLoading, isValidating, error, actionType]);
|
|
138
236
|
|
|
139
237
|
return result;
|
|
140
238
|
};
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
useLayoutType,
|
|
26
26
|
useVisit,
|
|
27
27
|
} from '@openmrs/esm-framework';
|
|
28
|
-
import React, { useCallback, useEffect } from 'react';
|
|
28
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
29
29
|
import { Controller, useForm } from 'react-hook-form';
|
|
30
30
|
import { useTranslation } from 'react-i18next';
|
|
31
31
|
import { mutate as mutateSWR } from 'swr';
|
|
@@ -33,7 +33,7 @@ import { z } from 'zod';
|
|
|
33
33
|
import styles from './discharge-body.scss';
|
|
34
34
|
import DeceasedInfo from '../../deceased-patient-header/deceasedInfo/deceased-info.component';
|
|
35
35
|
import { PatientInfo } from '../../types';
|
|
36
|
-
import {
|
|
36
|
+
import { useBlockDischargeWithPendingBills, usePersonAttributes } from './discharge-body.resource';
|
|
37
37
|
import { ConfigObject } from '../../config-schema';
|
|
38
38
|
import { getCurrentTime } from '../../utils/utils';
|
|
39
39
|
import { dischargeSchema } from '../../schemas';
|
|
@@ -44,47 +44,66 @@ import { useMortuaryOperation } from '../admit-deceased-person-workspace/admit-d
|
|
|
44
44
|
interface DischargeFormProps {
|
|
45
45
|
closeWorkspace: () => void;
|
|
46
46
|
patientUuid: string;
|
|
47
|
-
personUuid: string;
|
|
48
47
|
bedId: number;
|
|
49
48
|
mutate: () => void;
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
type DischargeFormValues = z.infer<typeof dischargeSchema>;
|
|
53
52
|
|
|
54
|
-
const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUuid, bedId,
|
|
53
|
+
const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUuid, bedId, mutate }) => {
|
|
55
54
|
const { t } = useTranslation();
|
|
56
55
|
const isTablet = useLayoutType() === 'tablet';
|
|
56
|
+
const [submissionError, setSubmissionError] = useState<string | null>(null);
|
|
57
|
+
|
|
57
58
|
const { activeVisit, currentVisitIsRetrospective } = useVisit(patientUuid);
|
|
58
59
|
const { queueEntry } = useVisitQueueEntry(patientUuid, activeVisit?.uuid);
|
|
60
|
+
|
|
59
61
|
const { dischargeBody, isLoadingEmrConfiguration } = useMortuaryOperation();
|
|
60
|
-
|
|
62
|
+
|
|
63
|
+
const {
|
|
64
|
+
createOrUpdatePersonAttribute,
|
|
65
|
+
personAttributes,
|
|
66
|
+
isLoading: isLoadingAttributes,
|
|
67
|
+
} = usePersonAttributes(patientUuid);
|
|
68
|
+
|
|
61
69
|
const { isDischargeBlocked, blockingMessage, isLoadingBills } = useBlockDischargeWithPendingBills({
|
|
62
70
|
patientUuid,
|
|
71
|
+
actionType: 'discharge',
|
|
63
72
|
});
|
|
64
73
|
|
|
65
|
-
const {
|
|
66
|
-
|
|
67
|
-
const { nextOfKinAddressUuid, nextOfKinNameUuid, nextOfKinPhoneUuid, nextOfKinRelationshipUuid } =
|
|
74
|
+
const { nextOfKinNameUuid, nextOfKinRelationshipUuid, nextOfKinPhoneUuid, nextOfKinAddressUuid } =
|
|
68
75
|
useConfig<ConfigObject>();
|
|
69
76
|
|
|
77
|
+
const { time: defaultTime, period: defaultPeriod } = getCurrentTime();
|
|
78
|
+
|
|
70
79
|
const getAttributeValue = useCallback(
|
|
71
80
|
(attributeTypeUuid: string) => {
|
|
72
|
-
if (!personAttributes) {
|
|
81
|
+
if (!personAttributes || !Array.isArray(personAttributes)) {
|
|
73
82
|
return '';
|
|
74
83
|
}
|
|
75
|
-
const
|
|
76
|
-
const attribute = attributes.find((attr) => attr.attributeType.uuid === attributeTypeUuid);
|
|
84
|
+
const attribute = personAttributes.find((attr) => attr.attributeType.uuid === attributeTypeUuid);
|
|
77
85
|
return attribute ? attribute.value : '';
|
|
78
86
|
},
|
|
79
87
|
[personAttributes],
|
|
80
88
|
);
|
|
81
89
|
|
|
90
|
+
const getExistingAttributeUuid = useCallback(
|
|
91
|
+
(attributeTypeUuid: string) => {
|
|
92
|
+
if (!personAttributes || !Array.isArray(personAttributes)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const attribute = personAttributes.find((attr) => attr.attributeType.uuid === attributeTypeUuid);
|
|
96
|
+
return attribute ? attribute.uuid : null;
|
|
97
|
+
},
|
|
98
|
+
[personAttributes],
|
|
99
|
+
);
|
|
100
|
+
|
|
82
101
|
const {
|
|
83
|
-
watch,
|
|
84
102
|
control,
|
|
85
103
|
setValue,
|
|
86
104
|
handleSubmit,
|
|
87
105
|
formState: { errors, isDirty, isSubmitting },
|
|
106
|
+
watch,
|
|
88
107
|
} = useForm<DischargeFormValues>({
|
|
89
108
|
resolver: zodResolver(dischargeSchema),
|
|
90
109
|
defaultValues: {
|
|
@@ -92,96 +111,176 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
92
111
|
timeOfDischarge: defaultTime,
|
|
93
112
|
period: defaultPeriod,
|
|
94
113
|
burialPermitNumber: '',
|
|
114
|
+
nextOfKinNames: '',
|
|
115
|
+
relationshipType: '',
|
|
116
|
+
nextOfKinContact: '',
|
|
117
|
+
nextOfKinAddress: '',
|
|
95
118
|
},
|
|
96
119
|
});
|
|
120
|
+
|
|
97
121
|
useEffect(() => {
|
|
98
122
|
if (Array.isArray(personAttributes) && personAttributes.length > 0) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
const initialValues = {
|
|
124
|
+
nextOfKinNames: getAttributeValue(nextOfKinNameUuid),
|
|
125
|
+
relationshipType: getAttributeValue(nextOfKinRelationshipUuid),
|
|
126
|
+
nextOfKinContact: getAttributeValue(nextOfKinPhoneUuid),
|
|
127
|
+
nextOfKinAddress: getAttributeValue(nextOfKinAddressUuid),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Object.entries(initialValues).forEach(([field, value]) => {
|
|
131
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
132
|
+
setValue(field as keyof DischargeFormValues, value);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
103
135
|
}
|
|
104
136
|
}, [
|
|
105
137
|
personAttributes,
|
|
138
|
+
setValue,
|
|
106
139
|
getAttributeValue,
|
|
107
140
|
nextOfKinNameUuid,
|
|
108
141
|
nextOfKinRelationshipUuid,
|
|
109
142
|
nextOfKinPhoneUuid,
|
|
110
143
|
nextOfKinAddressUuid,
|
|
111
|
-
setValue,
|
|
112
144
|
]);
|
|
145
|
+
|
|
113
146
|
const onSubmit = async (data: DischargeFormValues) => {
|
|
147
|
+
setSubmissionError(null);
|
|
148
|
+
|
|
114
149
|
if (currentVisitIsRetrospective) {
|
|
115
150
|
setCurrentVisit(null, null);
|
|
116
151
|
closeWorkspace();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// First, perform the discharge operation
|
|
157
|
+
await dischargeBody(activeVisit, queueEntry, bedId, data);
|
|
158
|
+
|
|
159
|
+
// Then update person attributes
|
|
160
|
+
const attributeUpdates = [
|
|
161
|
+
{
|
|
162
|
+
uuid: nextOfKinNameUuid,
|
|
163
|
+
value: data.nextOfKinNames,
|
|
164
|
+
existingUuid: getExistingAttributeUuid(nextOfKinNameUuid),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
uuid: nextOfKinRelationshipUuid,
|
|
168
|
+
value: data.relationshipType,
|
|
169
|
+
existingUuid: getExistingAttributeUuid(nextOfKinRelationshipUuid),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
uuid: nextOfKinPhoneUuid,
|
|
173
|
+
value: data.nextOfKinContact,
|
|
174
|
+
existingUuid: getExistingAttributeUuid(nextOfKinPhoneUuid),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
uuid: nextOfKinAddressUuid,
|
|
178
|
+
value: data.nextOfKinAddress,
|
|
179
|
+
existingUuid: getExistingAttributeUuid(nextOfKinAddressUuid),
|
|
180
|
+
},
|
|
181
|
+
].filter((attr) => attr.value !== undefined && attr.value !== null && attr.value !== '');
|
|
182
|
+
|
|
183
|
+
const patientInfo: PatientInfo = {
|
|
184
|
+
uuid: activeVisit.patient.uuid,
|
|
185
|
+
attributes: personAttributes || [],
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (const attr of attributeUpdates) {
|
|
189
|
+
try {
|
|
190
|
+
const attributeData: any = {
|
|
191
|
+
attributeType: attr.uuid,
|
|
192
|
+
value: attr.value,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (attr.existingUuid) {
|
|
196
|
+
attributeData.uuid = attr.existingUuid;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
await createOrUpdatePersonAttribute(patientUuid, attributeData, patientInfo);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
showSnackbar({
|
|
202
|
+
title: t('errorUpdatingAttribute', 'Error Updating Attribute'),
|
|
203
|
+
subtitle: t('errorUpdatingAttributeDescription', 'An error occurred while updating the attribute'),
|
|
204
|
+
kind: 'error',
|
|
205
|
+
isLowContrast: true,
|
|
206
|
+
});
|
|
135
207
|
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Invalidate relevant caches
|
|
211
|
+
await Promise.all([
|
|
212
|
+
mutateSWR((key) => typeof key === 'string' && key.startsWith(`${restBaseUrl}/visit`)),
|
|
213
|
+
mutateSWR((key) => typeof key === 'string' && key.startsWith(`${restBaseUrl}/patient`)),
|
|
214
|
+
mutateSWR((key) => typeof key === 'string' && key.startsWith(`${fhirBaseUrl}/Encounter`)),
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
showSnackbar({
|
|
218
|
+
title: t('dischargeDeceasedPatient', 'Deceased patient discharged'),
|
|
219
|
+
subtitle: t('deceasedPatientDischargedSuccessfully', 'Deceased patient has been discharged successfully'),
|
|
220
|
+
kind: 'success',
|
|
221
|
+
isLowContrast: true,
|
|
222
|
+
});
|
|
136
223
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
mutateSWR((key) => typeof key === 'string' && key.startsWith(`${fhirBaseUrl}/Encounter`));
|
|
148
|
-
|
|
149
|
-
mutate();
|
|
150
|
-
closeWorkspace();
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error(error);
|
|
153
|
-
const errorMessage = JSON.stringify(error?.responseBody?.error?.message?.replace(/\[/g, '').replace(/\]/g, ''));
|
|
154
|
-
showSnackbar({
|
|
155
|
-
title: t('visitError', 'Visit Error'),
|
|
156
|
-
subtitle: t(
|
|
157
|
-
'visitErrorMessage',
|
|
158
|
-
`An error has occurred while ending visit, Contact system administrator quoting this error ${errorMessage}`,
|
|
159
|
-
),
|
|
160
|
-
kind: 'error',
|
|
161
|
-
isLowContrast: true,
|
|
162
|
-
});
|
|
224
|
+
mutate();
|
|
225
|
+
closeWorkspace();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
let errorMessage = t('dischargeUnknownError', 'An unknown error occurred');
|
|
228
|
+
if (error?.message) {
|
|
229
|
+
errorMessage = error.message;
|
|
230
|
+
} else if (error?.responseBody?.error?.message) {
|
|
231
|
+
errorMessage = error.responseBody.error.message.replace(/\[|\]/g, '');
|
|
232
|
+
} else if (error?.responseBody?.error?.globalErrors) {
|
|
233
|
+
errorMessage = error.responseBody.error.globalErrors[0]?.message || errorMessage;
|
|
163
234
|
}
|
|
235
|
+
|
|
236
|
+
setSubmissionError(errorMessage);
|
|
237
|
+
showSnackbar({
|
|
238
|
+
title: t('dischargeError', 'Discharge Error'),
|
|
239
|
+
subtitle: errorMessage,
|
|
240
|
+
kind: 'error',
|
|
241
|
+
isLowContrast: true,
|
|
242
|
+
});
|
|
164
243
|
}
|
|
165
244
|
};
|
|
166
245
|
|
|
167
|
-
if (isLoadingEmrConfiguration || !
|
|
168
|
-
return <InlineLoading status="active" iconDescription="Loading" description=
|
|
246
|
+
if (isLoadingEmrConfiguration || isLoadingAttributes || !activeVisit) {
|
|
247
|
+
return <InlineLoading status="active" iconDescription="Loading" description={t('loading', 'Loading...')} />;
|
|
169
248
|
}
|
|
170
249
|
|
|
171
250
|
return (
|
|
172
251
|
<Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
|
|
173
252
|
<div className={styles.formContainer}>
|
|
253
|
+
{isLoadingBills && (
|
|
254
|
+
<InlineLoading
|
|
255
|
+
status="active"
|
|
256
|
+
iconDescription="Loading"
|
|
257
|
+
description={t('loadingBills', 'Loading bills...')}
|
|
258
|
+
/>
|
|
259
|
+
)}
|
|
260
|
+
|
|
261
|
+
{isDischargeBlocked && (
|
|
262
|
+
<InlineNotification
|
|
263
|
+
kind="warning"
|
|
264
|
+
title={t('warningMsg', 'Warning')}
|
|
265
|
+
subtitle={blockingMessage}
|
|
266
|
+
lowContrast={true}
|
|
267
|
+
className={styles.blockingNotification}
|
|
268
|
+
/>
|
|
269
|
+
)}
|
|
270
|
+
|
|
271
|
+
{submissionError && (
|
|
272
|
+
<InlineNotification
|
|
273
|
+
kind="error"
|
|
274
|
+
title={t('error', 'Error')}
|
|
275
|
+
subtitle={submissionError}
|
|
276
|
+
lowContrast={true}
|
|
277
|
+
className={styles.errorNotification}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
|
|
174
281
|
<Stack gap={3}>
|
|
175
282
|
<DeceasedInfo patientUuid={patientUuid} />
|
|
176
|
-
|
|
177
|
-
<InlineNotification
|
|
178
|
-
kind="error"
|
|
179
|
-
title={t('dischargeBlocked', 'Discharge Blocked')}
|
|
180
|
-
subtitle={blockingMessage}
|
|
181
|
-
lowContrast={true}
|
|
182
|
-
className={styles.blockingNotification}
|
|
183
|
-
/>
|
|
184
|
-
)}
|
|
283
|
+
|
|
185
284
|
<ResponsiveWrapper>
|
|
186
285
|
<div className={styles.dateTimePickerContainer}>
|
|
187
286
|
<Column>
|
|
@@ -200,9 +299,9 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
200
299
|
value={field.value ? new Date(field.value) : null}>
|
|
201
300
|
<DatePickerInput
|
|
202
301
|
{...field}
|
|
203
|
-
id="date-of-
|
|
302
|
+
id="date-of-discharge"
|
|
204
303
|
placeholder="yyyy-mm-dd"
|
|
205
|
-
labelText={t('
|
|
304
|
+
labelText={t('dateOfDischarge', 'Date of discharge*')}
|
|
206
305
|
invalid={!!errors.dateOfDischarge}
|
|
207
306
|
invalidText={errors.dateOfDischarge?.message}
|
|
208
307
|
/>
|
|
@@ -245,6 +344,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
245
344
|
</div>
|
|
246
345
|
</Column>
|
|
247
346
|
</div>
|
|
347
|
+
|
|
248
348
|
<Column className={styles.fieldColumn}>
|
|
249
349
|
<Controller
|
|
250
350
|
name="burialPermitNumber"
|
|
@@ -263,6 +363,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
263
363
|
)}
|
|
264
364
|
/>
|
|
265
365
|
</Column>
|
|
366
|
+
|
|
266
367
|
<Column className={styles.fieldColumn}>
|
|
267
368
|
<Controller
|
|
268
369
|
name="nextOfKinNames"
|
|
@@ -281,6 +382,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
281
382
|
)}
|
|
282
383
|
/>
|
|
283
384
|
</Column>
|
|
385
|
+
|
|
284
386
|
<Column className={styles.fieldColumn}>
|
|
285
387
|
<Controller
|
|
286
388
|
name="relationshipType"
|
|
@@ -288,17 +390,18 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
288
390
|
render={({ field }) => (
|
|
289
391
|
<TextInput
|
|
290
392
|
{...field}
|
|
291
|
-
id="
|
|
393
|
+
id="relationshipType"
|
|
292
394
|
type="text"
|
|
293
395
|
className={styles.sectionField}
|
|
294
|
-
placeholder={t('
|
|
295
|
-
labelText={t('
|
|
396
|
+
placeholder={t('relationshipType', 'Relationship')}
|
|
397
|
+
labelText={t('relationshipType', 'Relationship')}
|
|
296
398
|
invalid={!!errors.relationshipType}
|
|
297
399
|
invalidText={errors.relationshipType?.message}
|
|
298
400
|
/>
|
|
299
401
|
)}
|
|
300
402
|
/>
|
|
301
403
|
</Column>
|
|
404
|
+
|
|
302
405
|
<Column className={styles.fieldColumn}>
|
|
303
406
|
<Controller
|
|
304
407
|
name="nextOfKinContact"
|
|
@@ -306,17 +409,18 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
306
409
|
render={({ field }) => (
|
|
307
410
|
<TextInput
|
|
308
411
|
{...field}
|
|
309
|
-
id="
|
|
412
|
+
id="nextOfKinContact"
|
|
310
413
|
type="text"
|
|
311
|
-
className={styles.
|
|
312
|
-
placeholder={t('
|
|
313
|
-
labelText={t('
|
|
414
|
+
className={styles.sectionField}
|
|
415
|
+
placeholder={t('nextOfKinContact', 'Next of kin contact')}
|
|
416
|
+
labelText={t('nextOfKinContact', 'Next of kin contact')}
|
|
314
417
|
invalid={!!errors.nextOfKinContact}
|
|
315
418
|
invalidText={errors.nextOfKinContact?.message}
|
|
316
419
|
/>
|
|
317
420
|
)}
|
|
318
421
|
/>
|
|
319
422
|
</Column>
|
|
423
|
+
|
|
320
424
|
<Column className={styles.fieldColumn}>
|
|
321
425
|
<Controller
|
|
322
426
|
name="nextOfKinAddress"
|
|
@@ -336,6 +440,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
336
440
|
/>
|
|
337
441
|
</Column>
|
|
338
442
|
</ResponsiveWrapper>
|
|
443
|
+
|
|
339
444
|
<ResponsiveWrapper>
|
|
340
445
|
<Column>
|
|
341
446
|
<ExtensionSlot
|
|
@@ -349,6 +454,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
349
454
|
</ResponsiveWrapper>
|
|
350
455
|
</Stack>
|
|
351
456
|
</div>
|
|
457
|
+
|
|
352
458
|
<ButtonSet
|
|
353
459
|
className={classNames({
|
|
354
460
|
[styles.tablet]: isTablet,
|
|
@@ -364,7 +470,7 @@ const DischargeForm: React.FC<DischargeFormProps> = ({ closeWorkspace, patientUu
|
|
|
364
470
|
type="submit">
|
|
365
471
|
{isSubmitting ? (
|
|
366
472
|
<span className={styles.inlineLoading}>
|
|
367
|
-
{t('submitting', 'Submitting
|
|
473
|
+
{t('submitting', 'Submitting...')}
|
|
368
474
|
<InlineLoading status="active" iconDescription="Loading" />
|
|
369
475
|
</span>
|
|
370
476
|
) : (
|