@openmrs/esm-patient-chart-app 11.3.0 → 11.3.1-patch.9310
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 +27 -24
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/1815.js +2 -0
- package/dist/1815.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/2761.js +1 -1
- package/dist/2761.js.map +1 -1
- package/dist/2859.js +1 -1
- package/dist/2859.js.map +1 -1
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/3697.js +1 -0
- package/dist/3697.js.map +1 -0
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4618.js +1 -1
- package/dist/4632.js +1 -1
- package/dist/4632.js.map +1 -1
- package/dist/4652.js +1 -1
- package/dist/4718.js +1 -1
- package/dist/4754.js +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5205.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/5670.js +1 -0
- package/dist/5670.js.map +1 -0
- package/dist/5827.js +1 -0
- package/dist/5827.js.map +1 -0
- package/dist/6022.js +1 -1
- package/dist/6336.js +1 -0
- package/dist/6336.js.map +1 -0
- package/dist/6411.js +1 -1
- package/dist/6411.js.map +1 -1
- package/dist/6468.js +1 -1
- package/dist/6529.js +1 -1
- package/dist/6568.js +1 -0
- package/dist/6568.js.map +1 -0
- package/dist/6679.js +1 -1
- package/dist/68.js +2 -0
- package/dist/68.js.map +1 -0
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/6924.js +1 -0
- package/dist/6924.js.map +1 -0
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/7816.js +2 -0
- package/dist/7816.js.map +1 -0
- package/dist/7818.js +1 -0
- package/dist/7818.js.map +1 -0
- package/dist/7822.js +1 -0
- package/dist/7822.js.map +1 -0
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8260.js +1 -0
- package/dist/8260.js.map +1 -0
- package/dist/8278.js +1 -0
- package/dist/8278.js.map +1 -0
- package/dist/8349.js +1 -1
- package/dist/8454.js +1 -1
- package/dist/8454.js.map +1 -1
- package/dist/8618.js +1 -1
- package/dist/8709.js +1 -1
- package/dist/8709.js.map +1 -1
- package/dist/890.js +1 -1
- package/dist/9007.js +1 -1
- package/dist/9007.js.map +1 -1
- package/dist/9214.js +1 -1
- package/dist/{4727.js → 9294.js} +1 -1
- package/dist/9294.js.map +1 -0
- package/dist/9329.js +1 -0
- package/dist/9329.js.map +1 -0
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-chart-app.js +1 -1
- package/dist/openmrs-esm-patient-chart-app.js.buildmanifest.json +457 -453
- package/dist/openmrs-esm-patient-chart-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +5 -4
- package/src/actions-buttons/delete-visit.component.tsx +8 -3
- package/src/actions-buttons/mark-patient-deceased.component.tsx +2 -2
- package/src/actions-buttons/start-visit.component.tsx +10 -5
- package/src/actions-buttons/start-visit.test.tsx +9 -5
- package/src/actions-buttons/stop-visit.component.tsx +1 -1
- package/src/clinical-views/encounter-list/{encounter-list-tabs.component.tsx → encounter-list-tabs.extension.tsx} +10 -6
- package/src/clinical-views/encounter-list/tag.component.test.tsx +306 -0
- package/src/clinical-views/encounter-list/tag.component.tsx +27 -28
- package/src/clinical-views/encounter-tile/encounter-tile.component.tsx +7 -6
- package/src/clinical-views/encounter-tile/tile.scss +0 -1
- package/src/clinical-views/hooks/useEncountersByVisit.ts +13 -0
- package/src/clinical-views/hooks/useLastEncounter.ts +1 -1
- package/src/clinical-views/types.ts +2 -1
- package/src/clinical-views/utils/concept-utils.ts +24 -0
- package/src/clinical-views/utils/helpers.ts +2 -2
- package/src/clinical-views/utils/index.ts +4 -1
- package/src/config-schema.ts +42 -9
- package/src/dashboard.meta.ts +4 -2
- package/src/index.ts +21 -22
- package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +22 -11
- package/src/mark-patient-deceased/mark-patient-deceased-form.workspace.tsx +147 -138
- package/src/patient-banner-tags/{visit-attribute-tags.component.tsx → visit-attribute-tags.extension.tsx} +9 -4
- package/src/patient-chart/chart-review/dashboard-view.component.tsx +2 -2
- package/src/patient-chart/patient-chart.component.tsx +19 -36
- package/src/patient-chart/patient-chart.resources.ts +150 -0
- package/src/routes.json +17 -6
- package/src/visit/hooks/useDeleteVisit.test.tsx +39 -42
- package/src/visit/hooks/useDeleteVisit.tsx +33 -17
- package/src/visit/start-visit-button.component.tsx +2 -2
- package/src/visit/start-visit-button.test.tsx +2 -2
- package/src/visit/visit-action-items/edit-visit-details.component.tsx +29 -8
- package/src/visit/visit-form/base-visit-type.component.tsx +2 -2
- package/src/visit/visit-form/exported-visit-form.workspace.tsx +697 -0
- package/src/visit/visit-form/visit-attribute-type.component.tsx +2 -1
- package/src/visit/visit-form/visit-form.resource.ts +2 -1
- package/src/visit/visit-form/visit-form.test.tsx +28 -25
- package/src/visit/visit-form/visit-form.workspace.tsx +63 -643
- package/src/visit/visit-history-table/visit-actions-cell.component.tsx +3 -2
- package/src/visit/visit-history-table/visit-date-cell.component.tsx +1 -0
- package/src/visit/visit-history-table/visit-diagnoses-cell.component.tsx +1 -0
- package/src/visit/visit-history-table/visit-history-table.component.tsx +3 -2
- package/src/visit/visit-history-table/visit-type-cell.component.tsx +1 -0
- package/src/visit/visit-prompt/{delete-visit-dialog.component.tsx → delete-visit-dialog.modal.tsx} +10 -4
- package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +21 -3
- package/src/visit/visit-prompt/{end-visit-dialog.component.tsx → end-visit-dialog.modal.tsx} +7 -1
- package/src/visit/visit-prompt/end-visit-dialog.test.tsx +20 -1
- package/src/visit/visit-prompt/{start-visit-dialog.component.tsx → start-visit-dialog.modal.tsx} +10 -4
- package/src/visit/visit-prompt/start-visit-dialog.test.tsx +3 -3
- package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.tsx +7 -6
- package/src/visit/visits-widget/current-visit-summary.extension.tsx +48 -0
- package/src/visit/visits-widget/current-visit-summary.test.tsx +45 -25
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +15 -37
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.ts +0 -1
- package/src/visit/visits-widget/past-visits-components/medications-summary.component.tsx +2 -3
- package/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx +8 -1
- package/src/visit/visits-widget/past-visits-components/visit-summary.scss +1 -1
- package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.component.tsx +94 -0
- package/src/visit/visits-widget/single-visit-details/visit-timeline/visit-timeline.scss +60 -0
- package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +6 -7
- package/src/visit/visits-widget/visit-context/{visit-context-header.component.tsx → visit-context-header.extension.tsx} +17 -15
- package/src/visit/visits-widget/visit-context/visit-context-header.test.tsx +35 -29
- package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +15 -13
- package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +31 -9
- package/src/visit/visits-widget/visit-detail-overview.component.tsx +3 -2
- package/src/visit/visits-widget/visit-detail-overview.test.tsx +4 -4
- package/src/visit/visits-widget/visit.resource.tsx +1 -1
- package/translations/am.json +6 -0
- package/translations/ar.json +6 -0
- package/translations/ar_SY.json +6 -0
- package/translations/bn.json +6 -0
- package/translations/de.json +6 -0
- package/translations/en.json +7 -2
- package/translations/en_US.json +6 -0
- package/translations/es.json +6 -0
- package/translations/es_MX.json +6 -0
- package/translations/fr.json +15 -9
- package/translations/he.json +6 -0
- package/translations/hi.json +6 -0
- package/translations/hi_IN.json +6 -0
- package/translations/id.json +6 -0
- package/translations/it.json +24 -18
- package/translations/ka.json +6 -0
- package/translations/km.json +6 -0
- package/translations/ku.json +6 -0
- package/translations/ky.json +6 -0
- package/translations/lg.json +6 -0
- package/translations/ne.json +6 -0
- package/translations/pl.json +6 -0
- package/translations/pt.json +6 -0
- package/translations/pt_BR.json +6 -0
- package/translations/qu.json +6 -0
- package/translations/ro_RO.json +6 -0
- package/translations/ru_RU.json +6 -0
- package/translations/si.json +6 -0
- package/translations/sw.json +6 -0
- package/translations/sw_KE.json +6 -0
- package/translations/tr.json +6 -0
- package/translations/tr_TR.json +6 -0
- package/translations/uk.json +6 -0
- package/translations/uz.json +6 -0
- package/translations/uz@Latn.json +6 -0
- package/translations/uz_UZ.json +6 -0
- package/translations/vi.json +6 -0
- package/translations/zh.json +6 -0
- package/translations/zh_CN.json +6 -0
- package/dist/2537.js +0 -1
- package/dist/2537.js.map +0 -1
- package/dist/2735.js +0 -2
- package/dist/2735.js.map +0 -1
- package/dist/276.js +0 -1
- package/dist/276.js.map +0 -1
- package/dist/3042.js +0 -1
- package/dist/3042.js.map +0 -1
- package/dist/3119.js +0 -1
- package/dist/3119.js.map +0 -1
- package/dist/3184.js +0 -1
- package/dist/3184.js.map +0 -1
- package/dist/385.js +0 -2
- package/dist/385.js.map +0 -1
- package/dist/3905.js +0 -1
- package/dist/3905.js.map +0 -1
- package/dist/4713.js +0 -1
- package/dist/4713.js.map +0 -1
- package/dist/4727.js.map +0 -1
- package/dist/717.js +0 -1
- package/dist/717.js.map +0 -1
- package/dist/9162.js +0 -2
- package/dist/9162.js.map +0 -1
- package/dist/9206.js +0 -1
- package/dist/9206.js.map +0 -1
- package/dist/9615.js +0 -1
- package/dist/9615.js.map +0 -1
- package/src/visit/visits-widget/current-visit-summary.component.tsx +0 -55
- /package/dist/{9162.js.LICENSE.txt → 1815.js.LICENSE.txt} +0 -0
- /package/dist/{385.js.LICENSE.txt → 68.js.LICENSE.txt} +0 -0
- /package/dist/{2735.js.LICENSE.txt → 7816.js.LICENSE.txt} +0 -0
|
@@ -20,7 +20,7 @@ import { Controller, useForm, type SubmitHandler } from 'react-hook-form';
|
|
|
20
20
|
import { z } from 'zod';
|
|
21
21
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
22
22
|
import { WarningFilled } from '@carbon/react/icons';
|
|
23
|
-
import {
|
|
23
|
+
import { type PatientWorkspace2DefinitionProps, EmptyState } from '@openmrs/esm-patient-common-lib';
|
|
24
24
|
import {
|
|
25
25
|
ExtensionSlot,
|
|
26
26
|
useLayoutType,
|
|
@@ -28,12 +28,16 @@ import {
|
|
|
28
28
|
ResponsiveWrapper,
|
|
29
29
|
useConfig,
|
|
30
30
|
OpenmrsDatePicker,
|
|
31
|
+
Workspace2,
|
|
31
32
|
} from '@openmrs/esm-framework';
|
|
32
33
|
import { markPatientDeceased, useCausesOfDeath } from '../data.resource';
|
|
33
34
|
import { type ChartConfig } from '../config-schema';
|
|
34
35
|
import styles from './mark-patient-deceased-form.scss';
|
|
35
36
|
|
|
36
|
-
const MarkPatientDeceasedForm: React.FC<
|
|
37
|
+
const MarkPatientDeceasedForm: React.FC<PatientWorkspace2DefinitionProps<{}, {}>> = ({
|
|
38
|
+
closeWorkspace,
|
|
39
|
+
groupProps: { patientUuid },
|
|
40
|
+
}) => {
|
|
37
41
|
const { t } = useTranslation();
|
|
38
42
|
const isTablet = useLayoutType() === 'tablet';
|
|
39
43
|
const memoizedPatientUuid = useMemo(() => ({ patientUuid }), [patientUuid]);
|
|
@@ -78,7 +82,7 @@ const MarkPatientDeceasedForm: React.FC<DefaultPatientWorkspaceProps> = ({ close
|
|
|
78
82
|
|
|
79
83
|
const {
|
|
80
84
|
control,
|
|
81
|
-
formState: { errors, isSubmitting },
|
|
85
|
+
formState: { errors, isSubmitting, isDirty },
|
|
82
86
|
handleSubmit,
|
|
83
87
|
watch,
|
|
84
88
|
} = useForm<MarkPatientDeceasedFormSchema>({
|
|
@@ -117,151 +121,156 @@ const MarkPatientDeceasedForm: React.FC<DefaultPatientWorkspaceProps> = ({ close
|
|
|
117
121
|
const onError = (errors) => console.error(errors);
|
|
118
122
|
|
|
119
123
|
return (
|
|
120
|
-
<
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
{
|
|
124
|
+
<Workspace2 title={t('markPatientDeceased', 'Mark patient deceased')} hasUnsavedChanges={isDirty}>
|
|
125
|
+
<Form className={styles.form} onSubmit={handleSubmit(onSubmit, onError)}>
|
|
126
|
+
<div>
|
|
127
|
+
{isTablet && (
|
|
128
|
+
<Row className={styles.headerGridRow}>
|
|
129
|
+
<ExtensionSlot className={styles.dataGridRow} name="visit-form-header-slot" state={memoizedPatientUuid} />
|
|
130
|
+
</Row>
|
|
131
|
+
)}
|
|
132
|
+
<div className={styles.container}>
|
|
133
|
+
<span className={styles.warningContainer}>
|
|
134
|
+
<WarningFilled aria-label={t('warning', 'Warning')} className={styles.warningIcon} size={20} />
|
|
135
|
+
<span className={styles.warningText}>
|
|
136
|
+
{t(
|
|
137
|
+
'markDeceasedWarning',
|
|
138
|
+
'Marking the patient as deceased will end any active visits for this patient',
|
|
139
|
+
)}
|
|
140
|
+
</span>
|
|
132
141
|
</span>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<div className={styles.sectionTitle}>{t('dateOfDeath', 'Date of death')}</div>
|
|
136
|
-
{causesOfDeath?.length ? (
|
|
137
|
-
<ResponsiveWrapper>
|
|
138
|
-
<Controller
|
|
139
|
-
name="deathDate"
|
|
140
|
-
control={control}
|
|
141
|
-
render={({ field, fieldState }) => (
|
|
142
|
-
<OpenmrsDatePicker
|
|
143
|
-
{...field}
|
|
144
|
-
className={styles.datePicker}
|
|
145
|
-
id="deceasedDate"
|
|
146
|
-
data-testid="deceasedDate"
|
|
147
|
-
labelText={t('date', 'Date')}
|
|
148
|
-
maxDate={new Date()}
|
|
149
|
-
invalid={Boolean(fieldState?.error?.message)}
|
|
150
|
-
invalidText={fieldState?.error?.message}
|
|
151
|
-
/>
|
|
152
|
-
)}
|
|
153
|
-
/>
|
|
154
|
-
</ResponsiveWrapper>
|
|
155
|
-
) : (
|
|
156
|
-
<DatePickerSkeleton />
|
|
157
|
-
)}
|
|
158
|
-
</section>
|
|
159
|
-
<section>
|
|
160
|
-
<div className={styles.sectionTitle}>{t('causeOfDeath', 'Cause of death')}</div>
|
|
161
|
-
<div
|
|
162
|
-
className={classNames(styles.conceptAnswerOverviewWrapper, {
|
|
163
|
-
[styles.conceptAnswerOverviewWrapperTablet]: isTablet,
|
|
164
|
-
[styles.conceptAnswerOverviewWrapperDesktop]: !isTablet,
|
|
165
|
-
[styles.errorOutline]: errors?.causeOfDeath?.message,
|
|
166
|
-
})}
|
|
167
|
-
>
|
|
168
|
-
{isLoadingCausesOfDeath ? <StructuredListSkeleton /> : null}
|
|
169
|
-
|
|
142
|
+
<section>
|
|
143
|
+
<div className={styles.sectionTitle}>{t('dateOfDeath', 'Date of death')}</div>
|
|
170
144
|
{causesOfDeath?.length ? (
|
|
171
145
|
<ResponsiveWrapper>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
146
|
+
<Controller
|
|
147
|
+
name="deathDate"
|
|
148
|
+
control={control}
|
|
149
|
+
render={({ field, fieldState }) => (
|
|
150
|
+
<OpenmrsDatePicker
|
|
151
|
+
{...field}
|
|
152
|
+
className={styles.datePicker}
|
|
153
|
+
id="deceasedDate"
|
|
154
|
+
data-testid="deceasedDate"
|
|
155
|
+
labelText={t('date', 'Date')}
|
|
156
|
+
maxDate={new Date()}
|
|
157
|
+
invalid={Boolean(fieldState?.error?.message)}
|
|
158
|
+
invalidText={fieldState?.error?.message}
|
|
159
|
+
/>
|
|
160
|
+
)}
|
|
176
161
|
/>
|
|
177
162
|
</ResponsiveWrapper>
|
|
178
|
-
) :
|
|
163
|
+
) : (
|
|
164
|
+
<DatePickerSkeleton />
|
|
165
|
+
)}
|
|
166
|
+
</section>
|
|
167
|
+
<section>
|
|
168
|
+
<div className={styles.sectionTitle}>{t('causeOfDeath', 'Cause of death')}</div>
|
|
169
|
+
<div
|
|
170
|
+
className={classNames(styles.conceptAnswerOverviewWrapper, {
|
|
171
|
+
[styles.conceptAnswerOverviewWrapperTablet]: isTablet,
|
|
172
|
+
[styles.conceptAnswerOverviewWrapperDesktop]: !isTablet,
|
|
173
|
+
[styles.errorOutline]: errors?.causeOfDeath?.message,
|
|
174
|
+
})}
|
|
175
|
+
>
|
|
176
|
+
{isLoadingCausesOfDeath ? <StructuredListSkeleton /> : null}
|
|
179
177
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
? 'freeTextFieldCauseOfDeath'
|
|
190
|
-
: 'codedCauseOfDeath'
|
|
191
|
-
}
|
|
192
|
-
orientation="vertical"
|
|
193
|
-
onChange={onChange}
|
|
194
|
-
>
|
|
195
|
-
{filteredCausesOfDeath.map(({ uuid, display, name }) => (
|
|
196
|
-
<RadioButton
|
|
197
|
-
className={styles.radioButton}
|
|
198
|
-
id={name}
|
|
199
|
-
key={uuid}
|
|
200
|
-
labelText={display}
|
|
201
|
-
value={uuid}
|
|
202
|
-
/>
|
|
203
|
-
))}
|
|
204
|
-
</RadioButtonGroup>
|
|
205
|
-
)}
|
|
206
|
-
/>
|
|
207
|
-
) : null}
|
|
178
|
+
{causesOfDeath?.length ? (
|
|
179
|
+
<ResponsiveWrapper>
|
|
180
|
+
<Search
|
|
181
|
+
labelText={t('searchForCauseOfDeath', 'Search for a cause of death')}
|
|
182
|
+
onChange={handleSearchTermChange}
|
|
183
|
+
placeholder={t('searchForCauseOfDeath', 'Search for a cause of death')}
|
|
184
|
+
/>
|
|
185
|
+
</ResponsiveWrapper>
|
|
186
|
+
) : null}
|
|
208
187
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
188
|
+
{causesOfDeath?.length && filteredCausesOfDeath.length > 0 ? (
|
|
189
|
+
<Controller
|
|
190
|
+
name="causeOfDeath"
|
|
191
|
+
control={control}
|
|
192
|
+
render={({ field: { onChange } }) => (
|
|
193
|
+
<RadioButtonGroup
|
|
194
|
+
className={styles.radioButtonGroup}
|
|
195
|
+
name={
|
|
196
|
+
causeOfDeathValue === freeTextFieldConceptUuid
|
|
197
|
+
? 'freeTextFieldCauseOfDeath'
|
|
198
|
+
: 'codedCauseOfDeath'
|
|
199
|
+
}
|
|
200
|
+
orientation="vertical"
|
|
201
|
+
onChange={onChange}
|
|
202
|
+
>
|
|
203
|
+
{filteredCausesOfDeath.map(({ uuid, display, name }) => (
|
|
204
|
+
<RadioButton
|
|
205
|
+
className={styles.radioButton}
|
|
206
|
+
id={name}
|
|
207
|
+
key={uuid}
|
|
208
|
+
labelText={display}
|
|
209
|
+
value={uuid}
|
|
210
|
+
/>
|
|
211
|
+
))}
|
|
212
|
+
</RadioButtonGroup>
|
|
213
|
+
)}
|
|
214
|
+
/>
|
|
215
|
+
) : null}
|
|
221
216
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
placeholder={t('enterNonCodedCauseOfDeath', 'Enter non-coded cause of death')}
|
|
245
|
-
value={value}
|
|
246
|
-
/>
|
|
247
|
-
)}
|
|
248
|
-
/>
|
|
217
|
+
{searchTerm && filteredCausesOfDeath.length === 0 && (
|
|
218
|
+
<div className={styles.tileContainer}>
|
|
219
|
+
<Tile className={styles.tile}>
|
|
220
|
+
<div className={styles.tileContent}>
|
|
221
|
+
<p className={styles.content}>
|
|
222
|
+
{t('noMatchingCodedCausesOfDeath', 'No matching coded causes of death')}
|
|
223
|
+
</p>
|
|
224
|
+
<p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
225
|
+
</div>
|
|
226
|
+
</Tile>
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
{!isLoadingCausesOfDeath && !causesOfDeath?.length ? (
|
|
231
|
+
<EmptyState
|
|
232
|
+
displayText={t('causeOfDeath_lower', 'cause of death concepts configured in the system')}
|
|
233
|
+
headerTitle={t('causeOfDeath', 'Cause of death')}
|
|
234
|
+
/>
|
|
235
|
+
) : null}
|
|
236
|
+
</div>
|
|
237
|
+
{errors?.causeOfDeath && <p className={styles.errorMessage}>{errors?.causeOfDeath?.message}</p>}
|
|
238
|
+
</section>
|
|
249
239
|
</div>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
240
|
+
{causeOfDeathValue === freeTextFieldConceptUuid && (
|
|
241
|
+
<div className={styles.nonCodedCauseOfDeath}>
|
|
242
|
+
<Controller
|
|
243
|
+
name="nonCodedCauseOfDeath"
|
|
244
|
+
control={control}
|
|
245
|
+
render={({ field: { onChange, value } }) => (
|
|
246
|
+
<TextInput
|
|
247
|
+
id="freeTextCauseOfDeath"
|
|
248
|
+
invalid={!!errors?.nonCodedCauseOfDeath}
|
|
249
|
+
invalidText={errors?.nonCodedCauseOfDeath?.message}
|
|
250
|
+
labelText={t('nonCodedCauseOfDeath', 'Non-coded cause of death')}
|
|
251
|
+
onChange={onChange}
|
|
252
|
+
placeholder={t('enterNonCodedCauseOfDeath', 'Enter non-coded cause of death')}
|
|
253
|
+
value={value}
|
|
254
|
+
/>
|
|
255
|
+
)}
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
261
258
|
)}
|
|
262
|
-
</
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
</div>
|
|
260
|
+
<ButtonSet className={classNames({ [styles.tablet]: isTablet, [styles.desktop]: !isTablet })}>
|
|
261
|
+
<Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
|
|
262
|
+
{t('discard', 'Discard')}
|
|
263
|
+
</Button>
|
|
264
|
+
<Button className={styles.button} disabled={isSubmitting} kind="primary" type="submit">
|
|
265
|
+
{isSubmitting ? (
|
|
266
|
+
<InlineLoading description={t('saving', 'Saving') + '...'} role="progressbar" />
|
|
267
|
+
) : (
|
|
268
|
+
t('saveAndClose', 'Save and close')
|
|
269
|
+
)}
|
|
270
|
+
</Button>
|
|
271
|
+
</ButtonSet>
|
|
272
|
+
</Form>
|
|
273
|
+
</Workspace2>
|
|
265
274
|
);
|
|
266
275
|
};
|
|
267
276
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Tag } from '@carbon/react';
|
|
3
|
-
import { formatDate, useConfig } from '@openmrs/esm-framework';
|
|
4
|
-
import { useVisitOrOfflineVisit } from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import { formatDate, useConfig, useVisit } from '@openmrs/esm-framework';
|
|
5
4
|
import { type ChartConfig } from '../config-schema';
|
|
6
5
|
|
|
7
6
|
interface VisitAttributeTagsProps {
|
|
@@ -26,13 +25,19 @@ const getAttributeValue = (attributeType, value) => {
|
|
|
26
25
|
}
|
|
27
26
|
};
|
|
28
27
|
|
|
28
|
+
/**
|
|
29
|
+
* This extension slots to the patient-banner-tags-slot by default.
|
|
30
|
+
*/
|
|
29
31
|
const VisitAttributeTags: React.FC<VisitAttributeTagsProps> = ({ patientUuid }) => {
|
|
30
|
-
const {
|
|
32
|
+
const { activeVisit } = useVisit(patientUuid);
|
|
31
33
|
const { visitAttributeTypes } = useConfig<ChartConfig>();
|
|
32
34
|
|
|
35
|
+
if (activeVisit == null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
33
38
|
return (
|
|
34
39
|
<>
|
|
35
|
-
{
|
|
40
|
+
{activeVisit?.attributes
|
|
36
41
|
?.filter(
|
|
37
42
|
(attribute) =>
|
|
38
43
|
visitAttributeTypes.find(({ uuid }) => attribute?.attributeType?.uuid === uuid)?.displayInThePatientBanner,
|
|
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { useMatch } from 'react-router-dom';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import {
|
|
5
|
+
import { launchWorkspace2, Extension, ExtensionSlot, useExtensionSlotMeta } from '@openmrs/esm-framework';
|
|
6
6
|
import { launchStartVisitPrompt } from '@openmrs/esm-patient-common-lib';
|
|
7
7
|
import { dashboardPath } from '../../constants';
|
|
8
8
|
import styles from './dashboard-view.scss';
|
|
@@ -43,7 +43,7 @@ export function DashboardView({ dashboard, patientUuid, patient }: DashboardView
|
|
|
43
43
|
basePath: view,
|
|
44
44
|
patient,
|
|
45
45
|
patientUuid,
|
|
46
|
-
|
|
46
|
+
launchWorkspace2,
|
|
47
47
|
launchStartVisitPrompt,
|
|
48
48
|
}),
|
|
49
49
|
[patient, patientUuid, view],
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ExtensionSlot,
|
|
3
|
-
WorkspaceContainer,
|
|
4
|
-
setCurrentVisit,
|
|
5
|
-
usePatient,
|
|
6
|
-
useWorkspaces,
|
|
7
|
-
useLeftNav,
|
|
8
|
-
} from '@openmrs/esm-framework';
|
|
9
|
-
import { getPatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
1
|
+
import { ExtensionSlot, useWorkspaces, useLeftNav } from '@openmrs/esm-framework';
|
|
10
2
|
import classNames from 'classnames';
|
|
11
|
-
import React, {
|
|
3
|
+
import React, { useMemo, useState } from 'react';
|
|
12
4
|
import { useParams } from 'react-router-dom';
|
|
13
5
|
import { spaBasePath } from '../constants';
|
|
14
6
|
import Loader from '../loader/loader.component';
|
|
@@ -16,31 +8,27 @@ import ChartReview from '../patient-chart/chart-review/chart-review.component';
|
|
|
16
8
|
import SideMenuPanel from '../side-nav/side-menu.component';
|
|
17
9
|
import { type LayoutMode } from './chart-review/dashboard-view.component';
|
|
18
10
|
import styles from './patient-chart.scss';
|
|
11
|
+
import { usePatientChartPatientAndVisit } from './patient-chart.resources';
|
|
19
12
|
|
|
20
13
|
const PatientChart: React.FC = () => {
|
|
21
14
|
const { patientUuid, view: encodedView } = useParams();
|
|
15
|
+
|
|
16
|
+
// specify key to ensure that WrapPatientChart instance is re-created
|
|
17
|
+
// when we switch patient
|
|
18
|
+
return <WrappedPatientChart key={patientUuid} patientUuid={patientUuid} encodedView={encodedView} />;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
interface WrappedPatientChartProps {
|
|
22
|
+
patientUuid: string;
|
|
23
|
+
encodedView: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const WrappedPatientChart: React.FC<WrappedPatientChartProps> = ({ patientUuid, encodedView }) => {
|
|
22
27
|
const view = decodeURIComponent(encodedView);
|
|
23
|
-
const { isLoading: isLoadingPatient, patient } = usePatient(patientUuid);
|
|
24
|
-
const state = useMemo(() => ({ patient, patientUuid }), [patient, patientUuid]);
|
|
25
28
|
const { workspaceWindowState, active } = useWorkspaces();
|
|
26
29
|
const [layoutMode, setLayoutMode] = useState<LayoutMode>();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// patient search) must be updated in the callback, which is called when the patient
|
|
30
|
-
// chart unmounts.
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
setCurrentVisit(patientUuid, null);
|
|
33
|
-
return () => {
|
|
34
|
-
setCurrentVisit(null, null);
|
|
35
|
-
};
|
|
36
|
-
}, [patientUuid]);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
getPatientChartStore().setState({ ...state });
|
|
40
|
-
return () => {
|
|
41
|
-
getPatientChartStore().setState({});
|
|
42
|
-
};
|
|
43
|
-
}, [state]);
|
|
30
|
+
const state = usePatientChartPatientAndVisit(patientUuid);
|
|
31
|
+
const { isLoadingPatient, patient } = state;
|
|
44
32
|
|
|
45
33
|
const leftNavBasePath = useMemo(() => spaBasePath.replace(':patientUuid', patientUuid), [patientUuid]);
|
|
46
34
|
|
|
@@ -71,8 +59,8 @@ const PatientChart: React.FC = () => {
|
|
|
71
59
|
className={classNames(styles.chartReview, { [styles.widthContained]: layoutMode == 'contained' })}
|
|
72
60
|
>
|
|
73
61
|
<ChartReview
|
|
74
|
-
patient={
|
|
75
|
-
patientUuid={
|
|
62
|
+
patient={patient}
|
|
63
|
+
patientUuid={patientUuid}
|
|
76
64
|
view={view}
|
|
77
65
|
setDashboardLayoutMode={setLayoutMode}
|
|
78
66
|
/>
|
|
@@ -83,11 +71,6 @@ const PatientChart: React.FC = () => {
|
|
|
83
71
|
</div>
|
|
84
72
|
</>
|
|
85
73
|
</main>
|
|
86
|
-
<WorkspaceContainer
|
|
87
|
-
showSiderailAndBottomNav
|
|
88
|
-
contextKey={`patient/${patientUuid}`}
|
|
89
|
-
additionalWorkspaceProps={state}
|
|
90
|
-
/>
|
|
91
74
|
</>
|
|
92
75
|
);
|
|
93
76
|
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import useSWR from 'swr';
|
|
3
|
+
import {
|
|
4
|
+
closeWorkspaceGroup2,
|
|
5
|
+
launchWorkspaceGroup2,
|
|
6
|
+
openmrsFetch,
|
|
7
|
+
restBaseUrl,
|
|
8
|
+
usePatient,
|
|
9
|
+
useVisit,
|
|
10
|
+
type Visit,
|
|
11
|
+
} from '@openmrs/esm-framework';
|
|
12
|
+
import { type PatientWorkspaceGroupProps, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
13
|
+
|
|
14
|
+
const defaultVisitCustomRepresentation =
|
|
15
|
+
'custom:(uuid,display,voided,indication,startDatetime,stopDatetime,' +
|
|
16
|
+
'encounters:(uuid,display,encounterDatetime,' +
|
|
17
|
+
'form:(uuid,name),location:ref,' +
|
|
18
|
+
'encounterType:ref,' +
|
|
19
|
+
'encounterProviders:(uuid,display,' +
|
|
20
|
+
'provider:(uuid,display))),' +
|
|
21
|
+
'patient:(uuid,display),' +
|
|
22
|
+
'visitType:(uuid,name,display),' +
|
|
23
|
+
'attributes:(uuid,display,attributeType:(name,datatypeClassname,uuid),value),' +
|
|
24
|
+
'location:(uuid,name,display))';
|
|
25
|
+
|
|
26
|
+
export function useVisitByUuid(visitUuid: string | null, representation: string = defaultVisitCustomRepresentation) {
|
|
27
|
+
const url = `${restBaseUrl}/visit/${visitUuid}?v=${representation}`;
|
|
28
|
+
const { data, ...rest } = useSWR<{ data: Visit }>(visitUuid ? url : null, openmrsFetch);
|
|
29
|
+
return { visit: data?.data, ...rest };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* This hook manages fetching of the patient and the visitContext
|
|
34
|
+
* when entering the patient chart, and the associated updated to patient chart store.
|
|
35
|
+
*
|
|
36
|
+
* The patient chart store sets the patient when we enter the patient chart
|
|
37
|
+
* and unsets the patient when we leave. (This gives extensions and workspaces a way
|
|
38
|
+
* to check whether they are rendered within the patient chart app.)
|
|
39
|
+
* Note: We do not unset visitContext when leaving the chart, so it persists across
|
|
40
|
+
* in‑app navigation. On a full page reload, visitContext is rehydrated by refetching
|
|
41
|
+
* (via useVisit/useVisitByUuId) rather than restored from storage.
|
|
42
|
+
* When we enter the chart, we want to update the visit context as follows:
|
|
43
|
+
* does the the stored visitContext exist and belong to the patient?
|
|
44
|
+
* 1. If so, the visitContext should be valid but possibly stale; fetch the visit again
|
|
45
|
+
* and update the context
|
|
46
|
+
* 2. If not, fetch the active visit of the patient, If it exists, set it as the
|
|
47
|
+
* visitContext; otherwise, clear it.
|
|
48
|
+
* @param patientUuid
|
|
49
|
+
* @returns
|
|
50
|
+
*/
|
|
51
|
+
export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
52
|
+
const { isLoading: isLoadingPatient, patient } = usePatient(patientUuid);
|
|
53
|
+
const {
|
|
54
|
+
patientUuid: storePatientUuid,
|
|
55
|
+
setPatient,
|
|
56
|
+
visitContext,
|
|
57
|
+
mutateVisitContext,
|
|
58
|
+
setVisitContext,
|
|
59
|
+
} = usePatientChartStore(patientUuid);
|
|
60
|
+
|
|
61
|
+
const isVisitContextValid = visitContext && visitContext.patient.uuid === patientUuid;
|
|
62
|
+
const {
|
|
63
|
+
visit: newVisitContext,
|
|
64
|
+
mutate: newMutateVisitContext,
|
|
65
|
+
isValidating: isValidatingVisitContext,
|
|
66
|
+
} = useVisitByUuid(isVisitContextValid ? visitContext.uuid : null);
|
|
67
|
+
const {
|
|
68
|
+
activeVisit,
|
|
69
|
+
isValidating: isValidatingActiveVisit,
|
|
70
|
+
mutate: mutateActiveVisit,
|
|
71
|
+
} = useVisit(isVisitContextValid ? null : patientUuid);
|
|
72
|
+
|
|
73
|
+
const isWorkspaceGroupLaunched = useRef(false);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!isValidatingVisitContext && !isValidatingActiveVisit && patient) {
|
|
77
|
+
let groupProps: PatientWorkspaceGroupProps = null;
|
|
78
|
+
if (activeVisit) {
|
|
79
|
+
groupProps = {
|
|
80
|
+
patientUuid: patient.id,
|
|
81
|
+
patient,
|
|
82
|
+
visitContext: activeVisit,
|
|
83
|
+
mutateVisitContext: mutateActiveVisit,
|
|
84
|
+
};
|
|
85
|
+
} else if (newVisitContext) {
|
|
86
|
+
groupProps = {
|
|
87
|
+
patientUuid: patient.id,
|
|
88
|
+
patient,
|
|
89
|
+
visitContext: newVisitContext,
|
|
90
|
+
mutateVisitContext: newMutateVisitContext,
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
groupProps = {
|
|
94
|
+
patientUuid: patient.id,
|
|
95
|
+
patient,
|
|
96
|
+
visitContext: null,
|
|
97
|
+
mutateVisitContext: null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setVisitContext(groupProps.visitContext, groupProps.mutateVisitContext);
|
|
102
|
+
|
|
103
|
+
if (!isWorkspaceGroupLaunched.current) {
|
|
104
|
+
launchWorkspaceGroup2('patient-chart', groupProps);
|
|
105
|
+
isWorkspaceGroupLaunched.current = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, [
|
|
109
|
+
newVisitContext,
|
|
110
|
+
isValidatingVisitContext,
|
|
111
|
+
newMutateVisitContext,
|
|
112
|
+
setVisitContext,
|
|
113
|
+
activeVisit,
|
|
114
|
+
isValidatingActiveVisit,
|
|
115
|
+
storePatientUuid,
|
|
116
|
+
mutateActiveVisit,
|
|
117
|
+
isWorkspaceGroupLaunched,
|
|
118
|
+
patient,
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (!isLoadingPatient) {
|
|
123
|
+
setPatient(patient);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
setPatient(null);
|
|
128
|
+
};
|
|
129
|
+
}, [patient, setPatient, isLoadingPatient]);
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
return () => {
|
|
133
|
+
closeWorkspaceGroup2();
|
|
134
|
+
};
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
const state = useMemo(
|
|
138
|
+
() => ({
|
|
139
|
+
patientUuid,
|
|
140
|
+
patient: patient ?? {},
|
|
141
|
+
visitContext,
|
|
142
|
+
mutateVisitContext,
|
|
143
|
+
isLoadingPatient,
|
|
144
|
+
setPatient,
|
|
145
|
+
}),
|
|
146
|
+
[patient, patientUuid, visitContext, mutateVisitContext, isLoadingPatient, setPatient],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return state;
|
|
150
|
+
}
|
package/src/routes.json
CHANGED
|
@@ -225,18 +225,29 @@
|
|
|
225
225
|
"offline": true
|
|
226
226
|
}
|
|
227
227
|
],
|
|
228
|
-
"
|
|
228
|
+
"workspaces2": [
|
|
229
229
|
{
|
|
230
230
|
"name": "mark-patient-deceased-workspace-form",
|
|
231
231
|
"component": "markPatientDeceasedForm",
|
|
232
|
-
"
|
|
233
|
-
"type": "form"
|
|
232
|
+
"window": "mark-patient-deceased"
|
|
234
233
|
},
|
|
235
234
|
{
|
|
236
235
|
"name": "start-visit-workspace-form",
|
|
237
236
|
"component": "startVisitWorkspace",
|
|
238
|
-
"
|
|
239
|
-
"type": "start-visit"
|
|
237
|
+
"window": "start-visit"
|
|
240
238
|
}
|
|
241
|
-
]
|
|
239
|
+
],
|
|
240
|
+
"workspaceWindows2": [
|
|
241
|
+
{
|
|
242
|
+
"name": "start-visit",
|
|
243
|
+
"group": "patient-chart"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"name": "mark-patient-deceased",
|
|
247
|
+
"group": "patient-chart"
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"workspaceGroups2": [{
|
|
251
|
+
"name":"patient-chart"
|
|
252
|
+
}]
|
|
242
253
|
}
|