@openmrs/esm-patient-chart-app 11.3.1-patch.9310 → 11.3.1-patch.9508
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 +18 -21
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/2146.js +1 -1
- package/dist/2540.js +1 -0
- package/dist/2540.js.map +1 -0
- package/dist/2690.js +1 -1
- package/dist/276.js +1 -0
- package/dist/276.js.map +1 -0
- package/dist/2761.js.map +1 -1
- package/dist/3099.js +1 -1
- package/dist/{8278.js → 3119.js} +1 -1
- package/dist/{8278.js.map → 3119.js.map} +1 -1
- package/dist/3584.js +1 -1
- package/dist/3905.js +1 -0
- package/dist/3905.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/439.js +1 -0
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4944.js +1 -1
- package/dist/5048.js +1 -0
- package/dist/5048.js.map +1 -0
- package/dist/506.js +2 -0
- package/dist/506.js.map +1 -0
- package/dist/5173.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 -1
- package/dist/6022.js +1 -1
- package/dist/6411.js +1 -1
- package/dist/6411.js.map +1 -1
- package/dist/6468.js +1 -1
- package/dist/6568.js +1 -1
- package/dist/6568.js.map +1 -1
- package/dist/6589.js +1 -0
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/6924.js +1 -1
- package/dist/6924.js.map +1 -1
- package/dist/{9294.js → 6997.js} +1 -1
- package/dist/6997.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/7810.js +1 -0
- package/dist/7810.js.map +1 -0
- package/dist/7822.js +1 -1
- package/dist/7822.js.map +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8260.js +1 -1
- package/dist/8260.js.map +1 -1
- package/dist/8349.js +1 -1
- package/dist/8371.js +1 -0
- 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/9214.js +1 -1
- 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 +375 -285
- package/dist/openmrs-esm-patient-chart-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/actions-buttons/mark-patient-deceased.component.tsx +2 -2
- package/src/actions-buttons/start-visit.component.tsx +5 -10
- package/src/actions-buttons/start-visit.test.tsx +5 -9
- package/src/clinical-views/encounter-list/encounter-list-tabs.extension.tsx +2 -2
- package/src/clinical-views/utils/encounter-list-config-builder.ts +19 -6
- package/src/clinical-views/utils/helpers.ts +5 -4
- package/src/index.ts +18 -12
- package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +9 -15
- package/src/mark-patient-deceased/mark-patient-deceased-form.workspace.tsx +138 -147
- package/src/patient-banner-tags/visit-attribute-tags.extension.tsx +21 -13
- package/src/patient-banner-tags/visit-attribute-tags.scss +8 -0
- package/src/patient-chart/chart-review/dashboard-view.component.tsx +2 -2
- package/src/patient-chart/chart-review/dashboard-view.scss +5 -0
- package/src/patient-chart/patient-chart.component.tsx +41 -50
- package/src/patient-chart/patient-chart.resources.ts +10 -52
- package/src/routes.json +7 -18
- package/src/visit/hooks/useDeleteVisit.tsx +1 -1
- 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 +8 -29
- package/src/visit/visit-form/base-visit-type.component.tsx +30 -21
- package/src/visit/visit-form/exported-visit-form.workspace.tsx +3 -0
- package/src/visit/visit-form/visit-form.test.tsx +18 -27
- package/src/visit/visit-form/visit-form.workspace.tsx +653 -35
- package/src/visit/visit-history-table/visit-actions-cell.component.tsx +2 -3
- package/src/visit/visit-history-table/visit-date-cell.component.tsx +0 -1
- package/src/visit/visit-history-table/visit-diagnoses-cell.component.tsx +0 -1
- package/src/visit/visit-history-table/visit-history-table.component.tsx +2 -3
- package/src/visit/visit-history-table/visit-type-cell.component.tsx +0 -1
- package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +1 -1
- package/src/visit/visit-prompt/{end-visit-dialog.modal.tsx → end-visit-dialog.component.tsx} +1 -1
- package/src/visit/visit-prompt/end-visit-dialog.test.tsx +1 -1
- package/src/visit/visit-prompt/{start-visit-dialog.modal.tsx → start-visit-dialog.component.tsx} +4 -10
- package/src/visit/visit-prompt/start-visit-dialog.test.tsx +3 -3
- package/src/visit/visits-widget/current-visit-summary.extension.tsx +3 -3
- package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +35 -12
- package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +0 -1
- package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +2 -2
- package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +20 -22
- package/src/visit/visits-widget/visit-detail-overview.component.tsx +2 -3
- package/src/visit/visits-widget/visit-detail-overview.test.tsx +4 -4
- package/translations/am.json +1 -2
- package/translations/ar.json +2 -3
- package/translations/ar_SY.json +1 -2
- package/translations/bn.json +1 -2
- package/translations/cs.json +196 -0
- package/translations/de.json +1 -2
- package/translations/en.json +1 -1
- package/translations/en_US.json +1 -2
- package/translations/es.json +2 -3
- package/translations/es_MX.json +1 -2
- package/translations/fr.json +7 -8
- package/translations/he.json +2 -3
- package/translations/hi.json +1 -2
- package/translations/hi_IN.json +1 -2
- package/translations/id.json +2 -3
- package/translations/it.json +2 -3
- package/translations/ka.json +2 -3
- package/translations/km.json +2 -3
- package/translations/ku.json +1 -2
- package/translations/ky.json +1 -2
- package/translations/lg.json +1 -2
- package/translations/ne.json +1 -2
- package/translations/pl.json +1 -2
- package/translations/pt.json +2 -3
- package/translations/pt_BR.json +2 -3
- package/translations/qu.json +1 -2
- package/translations/ro_RO.json +2 -3
- package/translations/ru_RU.json +1 -2
- package/translations/si.json +1 -2
- package/translations/sq.json +196 -0
- package/translations/sw.json +1 -2
- package/translations/sw_KE.json +1 -2
- package/translations/tr.json +1 -2
- package/translations/tr_TR.json +1 -2
- package/translations/uk.json +1 -2
- package/translations/uz.json +1 -2
- package/translations/uz@Latn.json +1 -2
- package/translations/uz_UZ.json +1 -2
- package/translations/vi.json +2 -3
- package/translations/zh.json +2 -3
- package/translations/zh_CN.json +2 -3
- package/translations/zh_TW.json +196 -0
- package/dist/1815.js +0 -2
- package/dist/1815.js.map +0 -1
- package/dist/3697.js +0 -1
- package/dist/3697.js.map +0 -1
- package/dist/5827.js +0 -1
- package/dist/5827.js.map +0 -1
- package/dist/7818.js +0 -1
- package/dist/7818.js.map +0 -1
- package/dist/9294.js.map +0 -1
- package/dist/9329.js +0 -1
- package/dist/9329.js.map +0 -1
- /package/dist/{1815.js.LICENSE.txt → 506.js.LICENSE.txt} +0 -0
- /package/src/visit/visit-prompt/{delete-visit-dialog.modal.tsx → delete-visit-dialog.component.tsx} +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 { type
|
|
23
|
+
import { EmptyState, type DefaultPatientWorkspaceProps } from '@openmrs/esm-patient-common-lib';
|
|
24
24
|
import {
|
|
25
25
|
ExtensionSlot,
|
|
26
26
|
useLayoutType,
|
|
@@ -28,16 +28,12 @@ import {
|
|
|
28
28
|
ResponsiveWrapper,
|
|
29
29
|
useConfig,
|
|
30
30
|
OpenmrsDatePicker,
|
|
31
|
-
Workspace2,
|
|
32
31
|
} from '@openmrs/esm-framework';
|
|
33
32
|
import { markPatientDeceased, useCausesOfDeath } from '../data.resource';
|
|
34
33
|
import { type ChartConfig } from '../config-schema';
|
|
35
34
|
import styles from './mark-patient-deceased-form.scss';
|
|
36
35
|
|
|
37
|
-
const MarkPatientDeceasedForm: React.FC<
|
|
38
|
-
closeWorkspace,
|
|
39
|
-
groupProps: { patientUuid },
|
|
40
|
-
}) => {
|
|
36
|
+
const MarkPatientDeceasedForm: React.FC<DefaultPatientWorkspaceProps> = ({ closeWorkspace, patientUuid }) => {
|
|
41
37
|
const { t } = useTranslation();
|
|
42
38
|
const isTablet = useLayoutType() === 'tablet';
|
|
43
39
|
const memoizedPatientUuid = useMemo(() => ({ patientUuid }), [patientUuid]);
|
|
@@ -82,7 +78,7 @@ const MarkPatientDeceasedForm: React.FC<PatientWorkspace2DefinitionProps<{}, {}>
|
|
|
82
78
|
|
|
83
79
|
const {
|
|
84
80
|
control,
|
|
85
|
-
formState: { errors, isSubmitting
|
|
81
|
+
formState: { errors, isSubmitting },
|
|
86
82
|
handleSubmit,
|
|
87
83
|
watch,
|
|
88
84
|
} = useForm<MarkPatientDeceasedFormSchema>({
|
|
@@ -121,156 +117,151 @@ const MarkPatientDeceasedForm: React.FC<PatientWorkspace2DefinitionProps<{}, {}>
|
|
|
121
117
|
const onError = (errors) => console.error(errors);
|
|
122
118
|
|
|
123
119
|
return (
|
|
124
|
-
<
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{t(
|
|
137
|
-
'markDeceasedWarning',
|
|
138
|
-
'Marking the patient as deceased will end any active visits for this patient',
|
|
139
|
-
)}
|
|
140
|
-
</span>
|
|
120
|
+
<Form className={styles.form} onSubmit={handleSubmit(onSubmit, onError)}>
|
|
121
|
+
<div>
|
|
122
|
+
{isTablet && (
|
|
123
|
+
<Row className={styles.headerGridRow}>
|
|
124
|
+
<ExtensionSlot className={styles.dataGridRow} name="visit-form-header-slot" state={memoizedPatientUuid} />
|
|
125
|
+
</Row>
|
|
126
|
+
)}
|
|
127
|
+
<div className={styles.container}>
|
|
128
|
+
<span className={styles.warningContainer}>
|
|
129
|
+
<WarningFilled aria-label={t('warning', 'Warning')} className={styles.warningIcon} size={20} />
|
|
130
|
+
<span className={styles.warningText}>
|
|
131
|
+
{t('markDeceasedWarning', 'Marking the patient as deceased will end any active visits for this patient')}
|
|
141
132
|
</span>
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
</span>
|
|
134
|
+
<section>
|
|
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
|
+
|
|
144
170
|
{causesOfDeath?.length ? (
|
|
145
171
|
<ResponsiveWrapper>
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
)}
|
|
172
|
+
<Search
|
|
173
|
+
labelText={t('searchForCauseOfDeath', 'Search for a cause of death')}
|
|
174
|
+
onChange={handleSearchTermChange}
|
|
175
|
+
placeholder={t('searchForCauseOfDeath', 'Search for a cause of death')}
|
|
161
176
|
/>
|
|
162
177
|
</ResponsiveWrapper>
|
|
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}
|
|
178
|
+
) : null}
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
{causesOfDeath?.length && filteredCausesOfDeath.length > 0 ? (
|
|
181
|
+
<Controller
|
|
182
|
+
name="causeOfDeath"
|
|
183
|
+
control={control}
|
|
184
|
+
render={({ field: { onChange } }) => (
|
|
185
|
+
<RadioButtonGroup
|
|
186
|
+
className={styles.radioButtonGroup}
|
|
187
|
+
name={
|
|
188
|
+
causeOfDeathValue === freeTextFieldConceptUuid
|
|
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}
|
|
187
208
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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}
|
|
216
|
-
|
|
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
|
-
)}
|
|
209
|
+
{searchTerm && filteredCausesOfDeath.length === 0 && (
|
|
210
|
+
<div className={styles.tileContainer}>
|
|
211
|
+
<Tile className={styles.tile}>
|
|
212
|
+
<div className={styles.tileContent}>
|
|
213
|
+
<p className={styles.content}>
|
|
214
|
+
{t('noMatchingCodedCausesOfDeath', 'No matching coded causes of death')}
|
|
215
|
+
</p>
|
|
216
|
+
<p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
217
|
+
</div>
|
|
218
|
+
</Tile>
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
229
221
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</div>
|
|
237
|
-
{errors?.causeOfDeath && <p className={styles.errorMessage}>{errors?.causeOfDeath?.message}</p>}
|
|
238
|
-
</section>
|
|
239
|
-
</div>
|
|
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
|
-
/>
|
|
222
|
+
{!isLoadingCausesOfDeath && !causesOfDeath?.length ? (
|
|
223
|
+
<EmptyState
|
|
224
|
+
displayText={t('causeOfDeath_lower', 'cause of death concepts configured in the system')}
|
|
225
|
+
headerTitle={t('causeOfDeath', 'Cause of death')}
|
|
226
|
+
/>
|
|
227
|
+
) : null}
|
|
257
228
|
</div>
|
|
258
|
-
|
|
229
|
+
{errors?.causeOfDeath && <p className={styles.errorMessage}>{errors?.causeOfDeath?.message}</p>}
|
|
230
|
+
</section>
|
|
259
231
|
</div>
|
|
260
|
-
|
|
261
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
232
|
+
{causeOfDeathValue === freeTextFieldConceptUuid && (
|
|
233
|
+
<div className={styles.nonCodedCauseOfDeath}>
|
|
234
|
+
<Controller
|
|
235
|
+
name="nonCodedCauseOfDeath"
|
|
236
|
+
control={control}
|
|
237
|
+
render={({ field: { onChange, value } }) => (
|
|
238
|
+
<TextInput
|
|
239
|
+
id="freeTextCauseOfDeath"
|
|
240
|
+
invalid={!!errors?.nonCodedCauseOfDeath}
|
|
241
|
+
invalidText={errors?.nonCodedCauseOfDeath?.message}
|
|
242
|
+
labelText={t('nonCodedCauseOfDeath', 'Non-coded cause of death')}
|
|
243
|
+
onChange={onChange}
|
|
244
|
+
placeholder={t('enterNonCodedCauseOfDeath', 'Enter non-coded cause of death')}
|
|
245
|
+
value={value}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
<ButtonSet className={classNames({ [styles.tablet]: isTablet, [styles.desktop]: !isTablet })}>
|
|
253
|
+
<Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
|
|
254
|
+
{t('discard', 'Discard')}
|
|
255
|
+
</Button>
|
|
256
|
+
<Button className={styles.button} disabled={isSubmitting} kind="primary" type="submit">
|
|
257
|
+
{isSubmitting ? (
|
|
258
|
+
<InlineLoading description={t('saving', 'Saving') + '...'} role="progressbar" />
|
|
259
|
+
) : (
|
|
260
|
+
t('saveAndClose', 'Save and close')
|
|
261
|
+
)}
|
|
262
|
+
</Button>
|
|
263
|
+
</ButtonSet>
|
|
264
|
+
</Form>
|
|
274
265
|
);
|
|
275
266
|
};
|
|
276
267
|
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { Tag } from '@carbon/react';
|
|
3
3
|
import { formatDate, useConfig, useVisit } from '@openmrs/esm-framework';
|
|
4
4
|
import { type ChartConfig } from '../config-schema';
|
|
5
|
+
import styles from './visit-attribute-tags.scss';
|
|
5
6
|
|
|
6
7
|
interface VisitAttributeTagsProps {
|
|
7
8
|
patientUuid: string;
|
|
@@ -32,22 +33,29 @@ const VisitAttributeTags: React.FC<VisitAttributeTagsProps> = ({ patientUuid })
|
|
|
32
33
|
const { activeVisit } = useVisit(patientUuid);
|
|
33
34
|
const { visitAttributeTypes } = useConfig<ChartConfig>();
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
const displayableAttributes = activeVisit?.attributes
|
|
37
|
+
?.filter(
|
|
38
|
+
(attribute) =>
|
|
39
|
+
visitAttributeTypes?.find(({ uuid }) => attribute?.attributeType?.uuid === uuid)?.displayInThePatientBanner,
|
|
40
|
+
)
|
|
41
|
+
.map((attribute) => ({
|
|
42
|
+
attribute,
|
|
43
|
+
value: getAttributeValue(attribute?.attributeType, attribute?.value),
|
|
44
|
+
}))
|
|
45
|
+
.filter(({ value }) => value != null && value !== '');
|
|
46
|
+
|
|
47
|
+
if (!displayableAttributes?.length) {
|
|
36
48
|
return null;
|
|
37
49
|
}
|
|
50
|
+
|
|
38
51
|
return (
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
?.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<Tag key={attribute?.attributeType?.uuid} type="gray">
|
|
47
|
-
{getAttributeValue(attribute?.attributeType, attribute?.value)}
|
|
48
|
-
</Tag>
|
|
49
|
-
))}
|
|
50
|
-
</>
|
|
52
|
+
<div className={styles.tagsContainer}>
|
|
53
|
+
{displayableAttributes.map(({ attribute, value }) => (
|
|
54
|
+
<Tag key={attribute?.attributeType?.uuid} type="gray">
|
|
55
|
+
{value}
|
|
56
|
+
</Tag>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
51
59
|
);
|
|
52
60
|
};
|
|
53
61
|
|
|
@@ -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 { launchWorkspace, 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
|
+
launchWorkspace,
|
|
47
47
|
launchStartVisitPrompt,
|
|
48
48
|
}),
|
|
49
49
|
[patient, patientUuid, view],
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
grid-auto-rows: auto;
|
|
13
13
|
grid-gap: 1.3125rem;
|
|
14
14
|
margin: 1.3125rem;
|
|
15
|
+
// Hide empty extension wrappers when components return null
|
|
16
|
+
// This prevents empty space in the grid when supplementary widgets have no data
|
|
17
|
+
> :has([data-extension-id='immunization-detailed-history-card']:empty) {
|
|
18
|
+
display: none;
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
// See https://zpl.io/lrlmdq0 for the Visits dashboard design
|
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
import { ExtensionSlot, useWorkspaces, useLeftNav } from '@openmrs/esm-framework';
|
|
2
|
-
import classNames from 'classnames';
|
|
3
1
|
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
4
3
|
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { ExtensionSlot, WorkspaceContainer, useWorkspaces, useLeftNav } from '@openmrs/esm-framework';
|
|
5
5
|
import { spaBasePath } from '../constants';
|
|
6
|
-
import
|
|
6
|
+
import { usePatientChartPatientAndVisit } from './patient-chart.resources';
|
|
7
|
+
import { type LayoutMode } from './chart-review/dashboard-view.component';
|
|
7
8
|
import ChartReview from '../patient-chart/chart-review/chart-review.component';
|
|
9
|
+
import Loader from '../loader/loader.component';
|
|
8
10
|
import SideMenuPanel from '../side-nav/side-menu.component';
|
|
9
|
-
import { type LayoutMode } from './chart-review/dashboard-view.component';
|
|
10
11
|
import styles from './patient-chart.scss';
|
|
11
|
-
import { usePatientChartPatientAndVisit } from './patient-chart.resources';
|
|
12
12
|
|
|
13
13
|
const PatientChart: React.FC = () => {
|
|
14
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 }) => {
|
|
27
|
-
const view = decodeURIComponent(encodedView);
|
|
28
15
|
const { workspaceWindowState, active } = useWorkspaces();
|
|
29
16
|
const [layoutMode, setLayoutMode] = useState<LayoutMode>();
|
|
30
17
|
const state = usePatientChartPatientAndVisit(patientUuid);
|
|
31
|
-
const
|
|
18
|
+
const view = decodeURIComponent(encodedView);
|
|
32
19
|
|
|
33
20
|
const leftNavBasePath = useMemo(() => spaBasePath.replace(':patientUuid', patientUuid), [patientUuid]);
|
|
34
21
|
|
|
@@ -38,39 +25,43 @@ const WrappedPatientChart: React.FC<WrappedPatientChartProps> = ({ patientUuid,
|
|
|
38
25
|
<>
|
|
39
26
|
<SideMenuPanel />
|
|
40
27
|
<main className={classNames('omrs-main-content', styles.chartContainer)}>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<div
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/>
|
|
67
|
-
</div>
|
|
28
|
+
<div
|
|
29
|
+
className={classNames(
|
|
30
|
+
styles.innerChartContainer,
|
|
31
|
+
workspaceWindowState === 'normal' && active ? styles.closeWorkspace : styles.activeWorkspace,
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
{state.isLoadingPatient ? (
|
|
35
|
+
<Loader />
|
|
36
|
+
) : (
|
|
37
|
+
<>
|
|
38
|
+
<aside>
|
|
39
|
+
<ExtensionSlot name="patient-header-slot" state={state} />
|
|
40
|
+
<ExtensionSlot name="patient-highlights-bar-slot" state={state} />
|
|
41
|
+
<ExtensionSlot name="patient-info-slot" state={state} />
|
|
42
|
+
</aside>
|
|
43
|
+
<div className={styles.grid}>
|
|
44
|
+
<div
|
|
45
|
+
className={classNames(styles.chartReview, { [styles.widthContained]: layoutMode === 'contained' })}
|
|
46
|
+
>
|
|
47
|
+
<ChartReview
|
|
48
|
+
patient={state.patient}
|
|
49
|
+
patientUuid={state.patientUuid}
|
|
50
|
+
view={view}
|
|
51
|
+
setDashboardLayoutMode={setLayoutMode}
|
|
52
|
+
/>
|
|
68
53
|
</div>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
54
|
+
</div>
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
73
58
|
</main>
|
|
59
|
+
<WorkspaceContainer
|
|
60
|
+
actionMenuProps={state}
|
|
61
|
+
additionalWorkspaceProps={state}
|
|
62
|
+
contextKey={`patient/${patientUuid}`}
|
|
63
|
+
showSiderailAndBottomNav
|
|
64
|
+
/>
|
|
74
65
|
</>
|
|
75
66
|
);
|
|
76
67
|
};
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import { useEffect, useMemo
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
2
|
import useSWR from 'swr';
|
|
3
|
-
import {
|
|
4
|
-
|
|
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';
|
|
3
|
+
import { openmrsFetch, restBaseUrl, usePatient, useVisit, type Visit } from '@openmrs/esm-framework';
|
|
4
|
+
import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
|
|
13
5
|
|
|
14
6
|
const defaultVisitCustomRepresentation =
|
|
15
7
|
'custom:(uuid,display,voided,indication,startDatetime,stopDatetime,' +
|
|
@@ -40,7 +32,7 @@ export function useVisitByUuid(visitUuid: string | null, representation: string
|
|
|
40
32
|
* in‑app navigation. On a full page reload, visitContext is rehydrated by refetching
|
|
41
33
|
* (via useVisit/useVisitByUuId) rather than restored from storage.
|
|
42
34
|
* When we enter the chart, we want to update the visit context as follows:
|
|
43
|
-
* does the
|
|
35
|
+
* does the stored visitContext exist and belong to the patient?
|
|
44
36
|
* 1. If so, the visitContext should be valid but possibly stale; fetch the visit again
|
|
45
37
|
* and update the context
|
|
46
38
|
* 2. If not, fetch the active visit of the patient, If it exists, set it as the
|
|
@@ -58,7 +50,7 @@ export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
|
58
50
|
setVisitContext,
|
|
59
51
|
} = usePatientChartStore(patientUuid);
|
|
60
52
|
|
|
61
|
-
const isVisitContextValid = visitContext && visitContext.patient
|
|
53
|
+
const isVisitContextValid = visitContext && visitContext.patient?.uuid === patientUuid;
|
|
62
54
|
const {
|
|
63
55
|
visit: newVisitContext,
|
|
64
56
|
mutate: newMutateVisitContext,
|
|
@@ -70,39 +62,14 @@ export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
|
70
62
|
mutate: mutateActiveVisit,
|
|
71
63
|
} = useVisit(isVisitContextValid ? null : patientUuid);
|
|
72
64
|
|
|
73
|
-
const isWorkspaceGroupLaunched = useRef(false);
|
|
74
|
-
|
|
75
65
|
useEffect(() => {
|
|
76
|
-
if (!isValidatingVisitContext && !isValidatingActiveVisit &&
|
|
77
|
-
let groupProps: PatientWorkspaceGroupProps = null;
|
|
66
|
+
if (!isValidatingVisitContext && !isValidatingActiveVisit && storePatientUuid) {
|
|
78
67
|
if (activeVisit) {
|
|
79
|
-
|
|
80
|
-
patientUuid: patient.id,
|
|
81
|
-
patient,
|
|
82
|
-
visitContext: activeVisit,
|
|
83
|
-
mutateVisitContext: mutateActiveVisit,
|
|
84
|
-
};
|
|
68
|
+
setVisitContext(activeVisit, mutateActiveVisit);
|
|
85
69
|
} else if (newVisitContext) {
|
|
86
|
-
|
|
87
|
-
patientUuid: patient.id,
|
|
88
|
-
patient,
|
|
89
|
-
visitContext: newVisitContext,
|
|
90
|
-
mutateVisitContext: newMutateVisitContext,
|
|
91
|
-
};
|
|
70
|
+
setVisitContext(newVisitContext, newMutateVisitContext);
|
|
92
71
|
} else {
|
|
93
|
-
|
|
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;
|
|
72
|
+
setVisitContext(null, null);
|
|
106
73
|
}
|
|
107
74
|
}
|
|
108
75
|
}, [
|
|
@@ -114,8 +81,6 @@ export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
|
114
81
|
isValidatingActiveVisit,
|
|
115
82
|
storePatientUuid,
|
|
116
83
|
mutateActiveVisit,
|
|
117
|
-
isWorkspaceGroupLaunched,
|
|
118
|
-
patient,
|
|
119
84
|
]);
|
|
120
85
|
|
|
121
86
|
useEffect(() => {
|
|
@@ -128,12 +93,6 @@ export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
|
128
93
|
};
|
|
129
94
|
}, [patient, setPatient, isLoadingPatient]);
|
|
130
95
|
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
return () => {
|
|
133
|
-
closeWorkspaceGroup2();
|
|
134
|
-
};
|
|
135
|
-
}, []);
|
|
136
|
-
|
|
137
96
|
const state = useMemo(
|
|
138
97
|
() => ({
|
|
139
98
|
patientUuid,
|
|
@@ -141,9 +100,8 @@ export function usePatientChartPatientAndVisit(patientUuid: string) {
|
|
|
141
100
|
visitContext,
|
|
142
101
|
mutateVisitContext,
|
|
143
102
|
isLoadingPatient,
|
|
144
|
-
setPatient,
|
|
145
103
|
}),
|
|
146
|
-
[patient, patientUuid, visitContext, mutateVisitContext, isLoadingPatient
|
|
104
|
+
[patient, patientUuid, visitContext, mutateVisitContext, isLoadingPatient],
|
|
147
105
|
);
|
|
148
106
|
|
|
149
107
|
return state;
|