@kenyaemr/esm-service-queues-app 8.1.1-pre.124 → 8.1.2-pre.152
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 +26 -26
- package/dist/1006.js +1 -0
- package/dist/1006.js.map +1 -0
- package/dist/1060.js +1 -0
- package/dist/1060.js.map +1 -0
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/1325.js +1 -0
- package/dist/1325.js.map +1 -0
- package/dist/1644.js +1 -0
- package/dist/1727.js +1 -0
- package/dist/1727.js.map +1 -0
- package/dist/1800.js +1 -0
- package/dist/1800.js.map +1 -0
- package/dist/236.js +1 -1
- package/dist/2757.js +1 -0
- package/dist/{282.js → 2760.js} +1 -1
- package/dist/2760.js.map +1 -0
- package/dist/2784.js +2 -0
- package/dist/2784.js.map +1 -0
- package/dist/3199.js +1 -0
- package/dist/3199.js.map +1 -0
- package/dist/3372.js +2 -0
- package/dist/3372.js.map +1 -0
- package/dist/3574.js +1 -0
- package/dist/3604.js +1 -0
- package/dist/3604.js.map +1 -0
- package/dist/3652.js +1 -0
- package/dist/3760.js +1 -0
- package/dist/3760.js.map +1 -0
- package/dist/3818.js +1 -0
- package/dist/3818.js.map +1 -0
- package/dist/3828.js +1 -0
- package/dist/3828.js.map +1 -0
- package/dist/4272.js +1 -0
- package/dist/4378.js +1 -0
- package/dist/443.js +1 -0
- package/dist/443.js.map +1 -0
- package/dist/4460.js +1 -0
- package/dist/4705.js +1 -0
- package/dist/4911.js +1 -0
- package/dist/4911.js.map +1 -0
- package/dist/5236.js +1 -0
- package/dist/5236.js.map +1 -0
- package/dist/5240.js +1 -0
- package/dist/5282.js +1 -0
- package/dist/5282.js.map +1 -0
- package/dist/5336.js +1 -0
- package/dist/539.js +1 -0
- package/dist/5673.js +1 -0
- package/dist/5711.js +1 -0
- package/dist/5737.js +1 -0
- package/dist/5833.js +1 -0
- package/dist/6566.js +1 -0
- package/dist/6578.js +2 -0
- package/dist/{660.js.LICENSE.txt → 6578.js.LICENSE.txt} +5 -0
- package/dist/6578.js.map +1 -0
- package/dist/6591.js +2 -0
- package/dist/6591.js.map +1 -0
- package/dist/6670.js +1 -0
- package/dist/6670.js.map +1 -0
- package/dist/6727.js +1 -0
- package/dist/744.js +1 -0
- package/dist/752.js +1 -1
- package/dist/752.js.map +1 -1
- package/dist/7807.js +1 -0
- package/dist/8271.js +1 -0
- package/dist/8319.js +1 -0
- package/dist/8788.js +1 -0
- package/dist/899.js +1 -0
- package/dist/9261.js +1 -0
- package/dist/9392.js +1 -0
- package/dist/9392.js.map +1 -0
- package/dist/9993.js +1 -0
- package/dist/9993.js.map +1 -0
- package/dist/kenyaemr-esm-service-queues-app.js +1 -1
- package/dist/kenyaemr-esm-service-queues-app.js.buildmanifest.json +541 -234
- package/dist/kenyaemr-esm-service-queues-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +5 -0
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package-lock.json +5892 -0
- package/package.json +8 -5
- package/src/active-visits/active-visits-table.resource.ts +0 -62
- package/src/active-visits/change-status-dialog.component.tsx +10 -3
- package/src/active-visits/change-status-dialog.scss +29 -1
- package/src/active-visits/change-status-dialog.test.tsx +11 -3
- package/src/add-provider-queue-room/add-provider-queue-room.component.tsx +53 -54
- package/src/add-provider-queue-room/add-provider-queue-room.scss +28 -0
- package/src/add-provider-queue-room/add-provider-queue-room.test.tsx +1 -1
- package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.component.tsx +1 -0
- package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.scss +29 -0
- package/src/clear-queue-entries-dialog/clear-queue-entries-dialog.test.tsx +4 -1
- package/src/config-schema.ts +28 -8
- package/src/create-queue-entry/create-queue-entry.workspace.tsx +99 -0
- package/src/create-queue-entry/existing-visit-form/existing-visit-form.component.tsx +73 -0
- package/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx → create-queue-entry/queue-fields/queue-fields.component.tsx} +76 -50
- package/src/create-queue-entry/queue-fields/queue-fields.resource.ts +63 -0
- package/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.scss → create-queue-entry/queue-fields/queue-fields.scss} +0 -4
- package/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx → create-queue-entry/queue-fields/queue-fields.test.tsx} +37 -17
- package/src/create-queue-entry/queue-fields/visit-form-queue-fields.extension.tsx +31 -0
- package/src/hooks/useQueueEntries.ts +13 -12
- package/src/index.ts +8 -14
- package/src/patient-info/patient-info.component.tsx +10 -8
- package/src/patient-queue-header/patient-queue-header.component.tsx +18 -13
- package/src/patient-queue-metrics/metrics-header.component.tsx +4 -4
- package/src/queue-entry-table-components/queue-priority.component.tsx +16 -11
- package/src/queue-entry-table-components/queue-priority.scss +5 -0
- package/src/queue-entry-table-components/transition-entry.component.tsx +1 -1
- package/src/queue-patient-linelists/queue-linelist-base-table.component.tsx +1 -1
- package/src/queue-patient-linelists/queue-linelist-filter.workspace.tsx +13 -12
- package/src/queue-patient-linelists/scheduled-appointments-table.component.tsx +1 -1
- package/src/queue-rooms/queue-room-form.test.tsx +2 -2
- package/src/queue-rooms/queue-room-form.workspace.tsx +1 -1
- package/src/queue-screen/queue-screen.test.tsx +11 -4
- package/src/queue-services/queue-service-form.test.tsx +76 -16
- package/src/queue-services/queue-service-form.workspace.tsx +131 -131
- package/src/queue-services/queue-service.resource.ts +7 -2
- package/src/queue-table/cells/columns.resource.ts +10 -7
- package/src/queue-table/cells/queue-table-visit-attribute-queue-number-cell.component.tsx +1 -0
- package/src/queue-table/default-queue-table.component.tsx +13 -13
- package/src/queue-table/default-queue-table.test.tsx +3 -3
- package/src/queue-table/queue-entry-actions/queue-entry-actions-modal.scss +28 -0
- package/src/queue-table/queue-entry-actions/queue-entry-actions.modal.tsx +8 -2
- package/src/queue-table/queue-entry-actions/queue-entry-confirm-action.modal.tsx +2 -1
- package/src/queue-table/queue-entry-actions/queue-entry-confirm-action.scss +29 -0
- package/src/queue-table/queue-table.component.tsx +1 -1
- package/src/queue-table/queue-table.scss +12 -2
- package/src/queue-table/queue-table.test.tsx +10 -1
- package/src/remove-queue-entry-dialog/remove-queue-entry.component.tsx +16 -4
- package/src/remove-queue-entry-dialog/remove-queue-entry.scss +29 -0
- package/src/routes.json +4 -8
- package/src/transition-latest-queue-entry/transition-latest-queue-entry.resource.ts +2 -10
- package/src/transition-queue-entry/transition-queue-entry-dialog.component.tsx +41 -32
- package/src/transition-queue-entry/transition-queue-entry-dialog.scss +28 -0
- package/src/types/index.ts +0 -8
- package/src/views/queue-tables-for-all-statuses.component.tsx +12 -15
- package/translations/am.json +5 -78
- package/translations/ar.json +5 -78
- package/translations/de.json +235 -0
- package/translations/en.json +9 -86
- package/translations/es.json +3 -76
- package/translations/fr.json +5 -78
- package/translations/he.json +5 -78
- package/translations/hi.json +235 -0
- package/translations/hi_IN.json +235 -0
- package/translations/id.json +235 -0
- package/translations/it.json +235 -0
- package/translations/km.json +5 -78
- package/translations/ne.json +235 -0
- package/translations/pt.json +235 -0
- package/translations/pt_BR.json +235 -0
- package/translations/qu.json +235 -0
- package/translations/si.json +235 -0
- package/translations/sw.json +235 -0
- package/translations/sw_KE.json +235 -0
- package/translations/tr.json +235 -0
- package/translations/tr_TR.json +235 -0
- package/translations/uk.json +235 -0
- package/translations/vi.json +235 -0
- package/translations/zh.json +6 -79
- package/translations/zh_CN.json +5 -78
- package/dist/169.js +0 -1
- package/dist/169.js.map +0 -1
- package/dist/199.js +0 -1
- package/dist/199.js.map +0 -1
- package/dist/236.js.map +0 -1
- package/dist/271.js +0 -1
- package/dist/282.js.map +0 -1
- package/dist/319.js +0 -1
- package/dist/325.js +0 -1
- package/dist/325.js.map +0 -1
- package/dist/366.js +0 -1
- package/dist/366.js.map +0 -1
- package/dist/372.js +0 -2
- package/dist/372.js.map +0 -1
- package/dist/392.js +0 -1
- package/dist/392.js.map +0 -1
- package/dist/460.js +0 -1
- package/dist/501.js +0 -1
- package/dist/501.js.map +0 -1
- package/dist/574.js +0 -1
- package/dist/591.js +0 -2
- package/dist/591.js.map +0 -1
- package/dist/6.js +0 -1
- package/dist/6.js.map +0 -1
- package/dist/60.js +0 -1
- package/dist/60.js.map +0 -1
- package/dist/604.js +0 -1
- package/dist/604.js.map +0 -1
- package/dist/644.js +0 -1
- package/dist/660.js +0 -2
- package/dist/660.js.map +0 -1
- package/dist/670.js +0 -1
- package/dist/670.js.map +0 -1
- package/dist/727.js +0 -1
- package/dist/727.js.map +0 -1
- package/dist/748.js +0 -1
- package/dist/748.js.map +0 -1
- package/dist/757.js +0 -1
- package/dist/760.js +0 -1
- package/dist/760.js.map +0 -1
- package/dist/784.js +0 -2
- package/dist/784.js.map +0 -1
- package/dist/788.js +0 -1
- package/dist/800.js +0 -1
- package/dist/800.js.map +0 -1
- package/dist/807.js +0 -1
- package/dist/818.js +0 -1
- package/dist/818.js.map +0 -1
- package/dist/828.js +0 -1
- package/dist/828.js.map +0 -1
- package/dist/833.js +0 -1
- package/dist/911.js +0 -1
- package/dist/911.js.map +0 -1
- package/dist/940.js +0 -1
- package/dist/940.js.map +0 -1
- package/src/add-patient-toqueue/add-patient-toqueue-dialog.component.tsx +0 -228
- package/src/add-patient-toqueue/add-patient-toqueue-dialog.scss +0 -32
- package/src/patient-search/advanced-search.component.tsx +0 -191
- package/src/patient-search/advanced-search.scss +0 -154
- package/src/patient-search/advanced-search.test.tsx +0 -26
- package/src/patient-search/basic-search.component.tsx +0 -112
- package/src/patient-search/basic-search.scss +0 -139
- package/src/patient-search/basic-search.test.tsx +0 -18
- package/src/patient-search/empty-data-illustration.component.tsx +0 -41
- package/src/patient-search/hooks/useActivePatientEnrollment.tsx +0 -29
- package/src/patient-search/hooks/useDefaultLocation.ts +0 -14
- package/src/patient-search/hooks/usePatients.tsx +0 -25
- package/src/patient-search/hooks/useRecommendedVisitTypes.tsx +0 -35
- package/src/patient-search/hooks/useScheduledVisits.ts +0 -52
- package/src/patient-search/patient-scheduled-visits.component.tsx +0 -315
- package/src/patient-search/patient-scheduled-visits.scss +0 -131
- package/src/patient-search/patient-scheduled-visits.test.tsx +0 -39
- package/src/patient-search/patient-search.workspace.tsx +0 -135
- package/src/patient-search/search-illustration.component.tsx +0 -27
- package/src/patient-search/search-results.component.tsx +0 -75
- package/src/patient-search/search-results.scss +0 -80
- package/src/patient-search/search-results.test.tsx +0 -69
- package/src/patient-search/search.resource.ts +0 -10
- package/src/patient-search/visit-form/existing-visit-form.component.tsx +0 -112
- package/src/patient-search/visit-form/queue.resource.ts +0 -64
- package/src/patient-search/visit-form/visit-form.component.tsx +0 -337
- package/src/patient-search/visit-form/visit-type-selector.component.tsx +0 -153
- package/src/patient-search/visit-form/visit-type-selector.scss +0 -100
- package/src/patient-search/visit-form/visit-type-selector.test.tsx +0 -84
- package/src/visits-missing-inqueue/visits-missing-inqueue.component.tsx +0 -277
- package/src/visits-missing-inqueue/visits-missing-inqueue.resource.ts +0 -93
- package/src/visits-missing-inqueue/visits-missing-inqueue.scss +0 -111
- /package/dist/{784.js.LICENSE.txt → 2784.js.LICENSE.txt} +0 -0
- /package/dist/{372.js.LICENSE.txt → 3372.js.LICENSE.txt} +0 -0
- /package/dist/{591.js.LICENSE.txt → 6591.js.LICENSE.txt} +0 -0
- /package/src/{patient-search/patient-search.scss → create-queue-entry/create-queue-entry.scss} +0 -0
- /package/src/{patient-search/visit-form/visit-form.scss → create-queue-entry/existing-visit-form/existing-visit-form.scss} +0 -0
- /package/src/{patient-search → create-queue-entry}/hooks/useQueueLocations.tsx +0 -0
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
import dayjs from 'dayjs';
|
|
3
|
-
import head from 'lodash-es/head';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import {
|
|
6
|
-
Button,
|
|
7
|
-
ButtonSet,
|
|
8
|
-
ContentSwitcher,
|
|
9
|
-
DataTableSkeleton,
|
|
10
|
-
InlineLoading,
|
|
11
|
-
InlineNotification,
|
|
12
|
-
RadioTile,
|
|
13
|
-
Switch,
|
|
14
|
-
TileGroup,
|
|
15
|
-
} from '@carbon/react';
|
|
16
|
-
import {
|
|
17
|
-
formatDatetime,
|
|
18
|
-
type NewVisitPayload,
|
|
19
|
-
parseDate,
|
|
20
|
-
saveVisit,
|
|
21
|
-
showSnackbar,
|
|
22
|
-
toDateObjectStrict,
|
|
23
|
-
toOmrsIsoString,
|
|
24
|
-
useConfig,
|
|
25
|
-
useLayoutType,
|
|
26
|
-
useLocations,
|
|
27
|
-
useSession,
|
|
28
|
-
useVisit,
|
|
29
|
-
useVisitTypes,
|
|
30
|
-
} from '@openmrs/esm-framework';
|
|
31
|
-
import { type Appointment, SearchTypes } from '../types';
|
|
32
|
-
import { postQueueEntry } from './visit-form/queue.resource';
|
|
33
|
-
import { convertTime12to24 } from '../helpers/time-helpers';
|
|
34
|
-
import { useQueueLocations } from './hooks/useQueueLocations';
|
|
35
|
-
import { useQueues } from '../hooks/useQueues';
|
|
36
|
-
import { useMutateQueueEntries } from '../hooks/useQueueEntries';
|
|
37
|
-
import { type ConfigObject } from '../config-schema';
|
|
38
|
-
import styles from './patient-scheduled-visits.scss';
|
|
39
|
-
|
|
40
|
-
enum visitType {
|
|
41
|
-
RECENT = 'Recent',
|
|
42
|
-
FUTURE = 'Future',
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const ScheduledVisitsForVisitType: React.FC<{
|
|
46
|
-
visits;
|
|
47
|
-
visitType;
|
|
48
|
-
scheduledVisitHeader;
|
|
49
|
-
patientUuid;
|
|
50
|
-
closeWorkspace: () => void;
|
|
51
|
-
}> = ({ visits, scheduledVisitHeader, patientUuid, closeWorkspace }) => {
|
|
52
|
-
const { t } = useTranslation();
|
|
53
|
-
const [visitsIndex, setVisitsIndex] = useState(0);
|
|
54
|
-
const [hasPriority, setHasPriority] = useState(false);
|
|
55
|
-
const [userLocation, setUserLocation] = useState('');
|
|
56
|
-
const locations = useLocations();
|
|
57
|
-
const session = useSession();
|
|
58
|
-
const { queues, isLoading: isLoadingQueues } = useQueues(userLocation);
|
|
59
|
-
const { mutateQueueEntries } = useMutateQueueEntries();
|
|
60
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
61
|
-
const timeFormat = new Date().getHours() >= 12 ? 'PM' : 'AM';
|
|
62
|
-
const visitDate = new Date();
|
|
63
|
-
const visitTime = dayjs(new Date()).format('hh:mm');
|
|
64
|
-
const [appointment, setAppointment] = useState<Appointment>();
|
|
65
|
-
const [patientId, setPatientId] = useState('');
|
|
66
|
-
const allVisitTypes = useVisitTypes();
|
|
67
|
-
const { currentVisit } = useVisit(patientUuid);
|
|
68
|
-
const config = useConfig<ConfigObject>();
|
|
69
|
-
const visitQueueNumberAttributeUuid = config.visitQueueNumberAttributeUuid;
|
|
70
|
-
const { queueLocations } = useQueueLocations();
|
|
71
|
-
const selectedQueueLocation = queueLocations[0]?.id;
|
|
72
|
-
|
|
73
|
-
// TODO: This needs fixing, we cannot just take the first queue and assume that is what is wanted
|
|
74
|
-
const service = head(queues)?.uuid;
|
|
75
|
-
const defaultStatus = config.concepts.defaultStatusConceptUuid;
|
|
76
|
-
const priorities = queues.find((q) => q.uuid === service)?.allowedPriorities ?? [];
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (!userLocation && session?.sessionLocation !== null) {
|
|
80
|
-
setUserLocation(session?.sessionLocation?.uuid);
|
|
81
|
-
} else if (!userLocation && locations) {
|
|
82
|
-
setUserLocation(head(locations)?.uuid);
|
|
83
|
-
}
|
|
84
|
-
}, [session, locations, userLocation]);
|
|
85
|
-
|
|
86
|
-
const handleSubmit = useCallback(
|
|
87
|
-
(priority) => {
|
|
88
|
-
setIsSubmitting(true);
|
|
89
|
-
const [hours, minutes] = convertTime12to24(visitTime, timeFormat);
|
|
90
|
-
const visitType = [...allVisitTypes].shift().uuid;
|
|
91
|
-
|
|
92
|
-
const payload: NewVisitPayload = {
|
|
93
|
-
patient: patientId,
|
|
94
|
-
startDatetime: toDateObjectStrict(
|
|
95
|
-
toOmrsIsoString(
|
|
96
|
-
new Date(dayjs(visitDate).year(), dayjs(visitDate).month(), dayjs(visitDate).date(), hours, minutes),
|
|
97
|
-
),
|
|
98
|
-
),
|
|
99
|
-
visitType: visitType,
|
|
100
|
-
location: userLocation,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
if (currentVisit) {
|
|
104
|
-
showSnackbar({
|
|
105
|
-
title: t('startVisitError', 'Error starting visit'),
|
|
106
|
-
kind: 'error',
|
|
107
|
-
isLowContrast: false,
|
|
108
|
-
subtitle: t('patientHasActiveVisit', 'The patient already has an active visit'),
|
|
109
|
-
});
|
|
110
|
-
setIsSubmitting(false);
|
|
111
|
-
} else {
|
|
112
|
-
const abortController = new AbortController();
|
|
113
|
-
|
|
114
|
-
saveVisit(payload, abortController)
|
|
115
|
-
.then((response) => {
|
|
116
|
-
postQueueEntry(
|
|
117
|
-
response.data.uuid,
|
|
118
|
-
patientId,
|
|
119
|
-
priority,
|
|
120
|
-
defaultStatus,
|
|
121
|
-
service,
|
|
122
|
-
appointment,
|
|
123
|
-
selectedQueueLocation,
|
|
124
|
-
visitQueueNumberAttributeUuid,
|
|
125
|
-
)
|
|
126
|
-
.then(() => {
|
|
127
|
-
showSnackbar({
|
|
128
|
-
kind: 'success',
|
|
129
|
-
title: t('startAVisit', 'Start a visit'),
|
|
130
|
-
subtitle: t(
|
|
131
|
-
'startVisitQueueSuccessfully',
|
|
132
|
-
'Patient has been added to active visits list and queue.',
|
|
133
|
-
`${hours} : ${minutes}`,
|
|
134
|
-
),
|
|
135
|
-
});
|
|
136
|
-
closeWorkspace();
|
|
137
|
-
setIsSubmitting(false);
|
|
138
|
-
mutateQueueEntries();
|
|
139
|
-
})
|
|
140
|
-
.catch((error) => {
|
|
141
|
-
showSnackbar({
|
|
142
|
-
title: t('queueEntryError', 'Error adding patient to the queue'),
|
|
143
|
-
kind: 'error',
|
|
144
|
-
isLowContrast: false,
|
|
145
|
-
subtitle: error?.message,
|
|
146
|
-
});
|
|
147
|
-
setIsSubmitting(false);
|
|
148
|
-
});
|
|
149
|
-
})
|
|
150
|
-
.catch((error) => {
|
|
151
|
-
showSnackbar({
|
|
152
|
-
title: t('startVisitError', 'Error starting visit'),
|
|
153
|
-
kind: 'error',
|
|
154
|
-
isLowContrast: false,
|
|
155
|
-
subtitle: error?.message,
|
|
156
|
-
});
|
|
157
|
-
setIsSubmitting(false);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
[
|
|
162
|
-
visitTime,
|
|
163
|
-
timeFormat,
|
|
164
|
-
allVisitTypes,
|
|
165
|
-
patientId,
|
|
166
|
-
visitDate,
|
|
167
|
-
userLocation,
|
|
168
|
-
queues,
|
|
169
|
-
config.concepts.defaultStatusConceptUuid,
|
|
170
|
-
config.concepts.defaultPriorityConceptUuid,
|
|
171
|
-
currentVisit,
|
|
172
|
-
t,
|
|
173
|
-
priorities,
|
|
174
|
-
appointment,
|
|
175
|
-
selectedQueueLocation,
|
|
176
|
-
visitQueueNumberAttributeUuid,
|
|
177
|
-
closeWorkspace,
|
|
178
|
-
mutateQueueEntries,
|
|
179
|
-
],
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
if (visits) {
|
|
183
|
-
return (
|
|
184
|
-
<div className={styles.row}>
|
|
185
|
-
<p className={styles.heading}>{scheduledVisitHeader} </p>
|
|
186
|
-
{visits?.length > 0 ? (
|
|
187
|
-
<TileGroup name="tile-group" defaultSelected="default-selected">
|
|
188
|
-
{visits?.map((visit, ind) => {
|
|
189
|
-
return (
|
|
190
|
-
<RadioTile
|
|
191
|
-
value={visit.uuid}
|
|
192
|
-
key={visit.uuid}
|
|
193
|
-
className={styles.visitTile}
|
|
194
|
-
onClick={(e) => {
|
|
195
|
-
setHasPriority(true);
|
|
196
|
-
setVisitsIndex(ind);
|
|
197
|
-
setPatientId(visit?.patient?.uuid);
|
|
198
|
-
setAppointment(visit);
|
|
199
|
-
}}>
|
|
200
|
-
<div className={styles.helperText}>
|
|
201
|
-
<p className={styles.primaryText}>{visit.service?.name}</p>
|
|
202
|
-
<p className={styles.secondaryText}>
|
|
203
|
-
{' '}
|
|
204
|
-
{formatDatetime(parseDate(visit?.startDateTime))} · {visit.location?.name}{' '}
|
|
205
|
-
</p>
|
|
206
|
-
|
|
207
|
-
{!visit.service ? (
|
|
208
|
-
<DataTableSkeleton />
|
|
209
|
-
) : isLoadingQueues ? null : !priorities?.length ? (
|
|
210
|
-
<InlineNotification
|
|
211
|
-
className={styles.inlineNotification}
|
|
212
|
-
kind={'error'}
|
|
213
|
-
lowContrast
|
|
214
|
-
subtitle={t('configurePriorities', 'Please configure priorities to continue.')}
|
|
215
|
-
title={t('noPrioritiesConfigured', 'No priorities configured')}
|
|
216
|
-
/>
|
|
217
|
-
) : hasPriority && ind == visitsIndex ? (
|
|
218
|
-
<ContentSwitcher
|
|
219
|
-
size="sm"
|
|
220
|
-
selectedIndex={null}
|
|
221
|
-
className={styles.prioritySwitcher}
|
|
222
|
-
onChange={(e) => {
|
|
223
|
-
handleSubmit(e.name);
|
|
224
|
-
}}>
|
|
225
|
-
{priorities?.length > 0
|
|
226
|
-
? priorities.map(({ uuid, display }) => {
|
|
227
|
-
return <Switch name={uuid} text={display} />;
|
|
228
|
-
})
|
|
229
|
-
: null}
|
|
230
|
-
</ContentSwitcher>
|
|
231
|
-
) : null}
|
|
232
|
-
{hasPriority && ind == visitsIndex && isSubmitting ? (
|
|
233
|
-
<InlineLoading description={t('loading', 'Loading...')} />
|
|
234
|
-
) : null}
|
|
235
|
-
</div>
|
|
236
|
-
</RadioTile>
|
|
237
|
-
);
|
|
238
|
-
})}
|
|
239
|
-
</TileGroup>
|
|
240
|
-
) : (
|
|
241
|
-
<div className={styles.emptyAppointment}>
|
|
242
|
-
<p>{t('noAppointmentsFound', 'No appointments found')} </p>
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
return (
|
|
249
|
-
<div className={styles.emptyAppointment}>
|
|
250
|
-
<p className={styles.heading}> {scheduledVisitHeader} </p>
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
interface PatientScheduledVisitsProps {
|
|
256
|
-
appointments: { recentVisits: Appointment[]; futureVisits: Appointment[] };
|
|
257
|
-
toggleSearchType: (searchMode: SearchTypes) => void;
|
|
258
|
-
patientUuid: string;
|
|
259
|
-
closeWorkspace: () => void;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const PatientScheduledVisits: React.FC<PatientScheduledVisitsProps> = ({
|
|
263
|
-
appointments,
|
|
264
|
-
toggleSearchType,
|
|
265
|
-
patientUuid,
|
|
266
|
-
closeWorkspace,
|
|
267
|
-
}) => {
|
|
268
|
-
const { t } = useTranslation();
|
|
269
|
-
const isTablet = useLayoutType() === 'tablet';
|
|
270
|
-
|
|
271
|
-
return (
|
|
272
|
-
<div className={styles.container}>
|
|
273
|
-
<ScheduledVisitsForVisitType
|
|
274
|
-
visitType={visitType.RECENT}
|
|
275
|
-
visits={appointments?.recentVisits}
|
|
276
|
-
scheduledVisitHeader={t('recentScheduledVisits', '{{count}} visit(s) scheduled for +/- 7 days', {
|
|
277
|
-
count: appointments?.recentVisits?.length,
|
|
278
|
-
})}
|
|
279
|
-
patientUuid={patientUuid}
|
|
280
|
-
closeWorkspace={closeWorkspace}
|
|
281
|
-
/>
|
|
282
|
-
<ScheduledVisitsForVisitType
|
|
283
|
-
visitType={visitType.FUTURE}
|
|
284
|
-
visits={appointments?.futureVisits}
|
|
285
|
-
scheduledVisitHeader={t('futureScheduledVisits', '{{count}} visit(s) scheduled for dates in the future', {
|
|
286
|
-
count: appointments?.futureVisits?.length,
|
|
287
|
-
})}
|
|
288
|
-
patientUuid={patientUuid}
|
|
289
|
-
closeWorkspace={closeWorkspace}
|
|
290
|
-
/>
|
|
291
|
-
|
|
292
|
-
<div className={styles['text-divider']}>{t('orInProperFormat', 'Or')}</div>
|
|
293
|
-
|
|
294
|
-
<div className={styles.buttonContainer}>
|
|
295
|
-
<Button
|
|
296
|
-
kind="ghost"
|
|
297
|
-
iconDescription="Start another visit type"
|
|
298
|
-
onClick={() => toggleSearchType(SearchTypes.VISIT_FORM)}>
|
|
299
|
-
{t('anotherVisitType', 'Start another visit type')}
|
|
300
|
-
</Button>
|
|
301
|
-
</div>
|
|
302
|
-
|
|
303
|
-
<ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
|
|
304
|
-
<Button className={styles.button} kind="secondary" onClick={() => toggleSearchType(SearchTypes.SEARCH_RESULTS)}>
|
|
305
|
-
{t('cancel', 'Cancel')}
|
|
306
|
-
</Button>
|
|
307
|
-
<Button className={styles.button} kind="primary" type="submit">
|
|
308
|
-
{t('search', 'Search')}
|
|
309
|
-
</Button>
|
|
310
|
-
</ButtonSet>
|
|
311
|
-
</div>
|
|
312
|
-
);
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
export default PatientScheduledVisits;
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
@use '@carbon/layout';
|
|
2
|
-
@use '@carbon/type';
|
|
3
|
-
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
|
-
|
|
5
|
-
.container {
|
|
6
|
-
background-color: $ui-background;
|
|
7
|
-
display: flex;
|
|
8
|
-
flex-direction: column;
|
|
9
|
-
justify-content: space-between;
|
|
10
|
-
height: 100%;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.grid {
|
|
14
|
-
padding: 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.visitTile {
|
|
18
|
-
margin: layout.$spacing-05 0;
|
|
19
|
-
|
|
20
|
-
&:global(.cds--tile--is-selected:focus) {
|
|
21
|
-
background-color: $color-blue-10;
|
|
22
|
-
color: $color-blue-10;
|
|
23
|
-
border: 1px solid $color-blue-60-2;
|
|
24
|
-
outline: none;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
&:global(.cds--tile--is-selected:hover) {
|
|
28
|
-
background-color: $color-blue-10;
|
|
29
|
-
color: $color-blue-10;
|
|
30
|
-
border: 1px solid $color-blue-60-2;
|
|
31
|
-
outline: none;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
&:global(.cds--tile--is-selected:hover .cds--tile__checkmark svg) {
|
|
35
|
-
fill: $color-blue-60-2;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.recentlyScheduledVisitsContainer {
|
|
40
|
-
margin-top: layout.$spacing-06;
|
|
41
|
-
margin-bottom: layout.$spacing-10;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.row {
|
|
45
|
-
margin: 0 layout.$spacing-05 layout.$spacing-05 layout.$spacing-05;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.backButton {
|
|
49
|
-
align-items: center;
|
|
50
|
-
display: flex;
|
|
51
|
-
justify-content: flex-start;
|
|
52
|
-
margin: layout.$spacing-03 0;
|
|
53
|
-
padding: 0;
|
|
54
|
-
@include type.type-style('body-compact-01');
|
|
55
|
-
|
|
56
|
-
button {
|
|
57
|
-
display: flex;
|
|
58
|
-
|
|
59
|
-
svg {
|
|
60
|
-
order: 1;
|
|
61
|
-
margin-right: layout.$spacing-03;
|
|
62
|
-
margin-left: 0 !important;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
span {
|
|
66
|
-
order: 2;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.heading {
|
|
72
|
-
@include type.type-style('body-02');
|
|
73
|
-
color: $text-02;
|
|
74
|
-
margin-left: 1.2rem;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.primaryText {
|
|
78
|
-
@include type.type-style('heading-compact-02');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.secondaryText {
|
|
82
|
-
@include type.type-style('body-01');
|
|
83
|
-
color: $text-02;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.button {
|
|
87
|
-
height: layout.$spacing-10;
|
|
88
|
-
display: flex;
|
|
89
|
-
align-content: flex-start;
|
|
90
|
-
align-items: baseline;
|
|
91
|
-
min-width: 50%;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.text-divider {
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
margin: layout.$spacing-08 25%;
|
|
98
|
-
@include type.type-style('heading-03');
|
|
99
|
-
color: $text-02;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.text-divider::before,
|
|
103
|
-
.text-divider::after {
|
|
104
|
-
content: '';
|
|
105
|
-
height: 1px;
|
|
106
|
-
background-color: $text-03;
|
|
107
|
-
flex-grow: 1;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.text-divider::before {
|
|
111
|
-
margin-right: layout.$spacing-05;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.text-divider::after {
|
|
115
|
-
margin-left: layout.$spacing-05;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.buttonContainer {
|
|
119
|
-
display: flex;
|
|
120
|
-
align-items: center;
|
|
121
|
-
justify-content: center;
|
|
122
|
-
margin-bottom: layout.$spacing-03;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.prioritySwitcher {
|
|
126
|
-
margin: layout.$spacing-03 0 layout.$spacing-03;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.emptyAppointment {
|
|
130
|
-
margin: layout.$spacing-05;
|
|
131
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { screen } from '@testing-library/react';
|
|
3
|
-
import { getDefaultsFromConfigSchema, useConfig, useLocations, useSession } from '@openmrs/esm-framework';
|
|
4
|
-
import { renderWithSwr } from 'tools';
|
|
5
|
-
import { type ConfigObject, configSchema } from '../config-schema';
|
|
6
|
-
import { mockLocations, mockPatient, mockPatientsVisits, mockSession } from '__mocks__';
|
|
7
|
-
import PatientScheduledVisits from './patient-scheduled-visits.component';
|
|
8
|
-
|
|
9
|
-
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
|
|
10
|
-
const mockToggleSearchType = jest.fn();
|
|
11
|
-
const mockUseLocations = useLocations as jest.Mock;
|
|
12
|
-
const mockUseSession = jest.mocked(useSession);
|
|
13
|
-
|
|
14
|
-
const defaultProps = {
|
|
15
|
-
appointments: { recentVisits: mockPatientsVisits.recentVisits, futureVisits: [] },
|
|
16
|
-
closePanel: () => false,
|
|
17
|
-
closeWorkspace: jest.fn(),
|
|
18
|
-
patientUuid: mockPatient.uuid,
|
|
19
|
-
toggleSearchType: mockToggleSearchType,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
describe('ScheduledVisits', () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
mockUseConfig.mockReturnValue({
|
|
25
|
-
...getDefaultsFromConfigSchema(configSchema),
|
|
26
|
-
concepts: {},
|
|
27
|
-
} as ConfigObject);
|
|
28
|
-
mockUseLocations.mockReturnValue(mockLocations.data.results);
|
|
29
|
-
mockUseSession.mockReturnValue(mockSession.data);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should display recent and future scheduled visits', async () => {
|
|
33
|
-
renderWithSwr(<PatientScheduledVisits {...defaultProps} />);
|
|
34
|
-
|
|
35
|
-
expect(screen.getByText(/Cardiology Consultation 1/i)).toBeInTheDocument();
|
|
36
|
-
expect(screen.getByText(/08-Aug-2022, 02:56 PM · 10 Engineer VCT/i)).toBeInTheDocument();
|
|
37
|
-
expect(screen.getByText(/No appointments found/i)).toBeInTheDocument();
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
import { SearchTypes } from '../types';
|
|
4
|
-
import PatientScheduledVisits from './patient-scheduled-visits.component';
|
|
5
|
-
import VisitForm from './visit-form/visit-form.component';
|
|
6
|
-
import {
|
|
7
|
-
type DefaultWorkspaceProps,
|
|
8
|
-
ArrowLeftIcon,
|
|
9
|
-
ErrorState,
|
|
10
|
-
getPatientName,
|
|
11
|
-
PatientBannerContactDetails,
|
|
12
|
-
PatientBannerPatientInfo,
|
|
13
|
-
PatientBannerToggleContactDetailsButton,
|
|
14
|
-
PatientPhoto,
|
|
15
|
-
usePatient,
|
|
16
|
-
useVisit,
|
|
17
|
-
} from '@openmrs/esm-framework';
|
|
18
|
-
import ExistingVisitFormComponent from './visit-form/existing-visit-form.component';
|
|
19
|
-
import styles from './patient-search.scss';
|
|
20
|
-
import { Button, DataTableSkeleton } from '@carbon/react';
|
|
21
|
-
import { useScheduledVisits } from './hooks/useScheduledVisits';
|
|
22
|
-
import isNil from 'lodash-es/isNil';
|
|
23
|
-
import { useTranslation } from 'react-i18next';
|
|
24
|
-
|
|
25
|
-
interface PatientSearchProps extends DefaultWorkspaceProps {
|
|
26
|
-
selectedPatientUuid: string;
|
|
27
|
-
currentServiceQueueUuid?: string;
|
|
28
|
-
handleBackToSearchList?: () => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const AddPatientToQueueContext = React.createContext({
|
|
32
|
-
currentServiceQueueUuid: '',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const PatientSearch: React.FC<PatientSearchProps> = ({
|
|
36
|
-
closeWorkspace,
|
|
37
|
-
selectedPatientUuid,
|
|
38
|
-
currentServiceQueueUuid,
|
|
39
|
-
handleBackToSearchList,
|
|
40
|
-
}) => {
|
|
41
|
-
const { t } = useTranslation();
|
|
42
|
-
const { patient } = usePatient(selectedPatientUuid);
|
|
43
|
-
const { activeVisit } = useVisit(selectedPatientUuid);
|
|
44
|
-
const [searchType, setSearchType] = useState<SearchTypes>(SearchTypes.SCHEDULED_VISITS);
|
|
45
|
-
const [showContactDetails, setContactDetails] = useState(false);
|
|
46
|
-
const { appointments, isLoading, error } = useScheduledVisits(selectedPatientUuid);
|
|
47
|
-
|
|
48
|
-
const hasAppointments = !(isNil(appointments?.futureVisits) && isNil(appointments?.recentVisits));
|
|
49
|
-
|
|
50
|
-
const backButtonDescription =
|
|
51
|
-
searchType === SearchTypes.VISIT_FORM && hasAppointments
|
|
52
|
-
? t('backToScheduledVisits', 'Back to scheduled visits')
|
|
53
|
-
: t('backToSearchResults', 'Back to search results');
|
|
54
|
-
|
|
55
|
-
const toggleSearchType = (searchType: SearchTypes) => {
|
|
56
|
-
setSearchType(searchType);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const handleBackToAction = () => {
|
|
60
|
-
if (searchType === SearchTypes.VISIT_FORM && hasAppointments) {
|
|
61
|
-
setSearchType(SearchTypes.SCHEDULED_VISITS);
|
|
62
|
-
} else {
|
|
63
|
-
setSearchType(SearchTypes.SEARCH_RESULTS);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
if (searchType === SearchTypes.SCHEDULED_VISITS && appointments && !hasAppointments) {
|
|
69
|
-
setSearchType(SearchTypes.VISIT_FORM);
|
|
70
|
-
}
|
|
71
|
-
}, [hasAppointments, appointments]);
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
if (searchType === SearchTypes.SEARCH_RESULTS) {
|
|
75
|
-
handleBackToSearchList && handleBackToSearchList();
|
|
76
|
-
}
|
|
77
|
-
}, [searchType, handleBackToSearchList]);
|
|
78
|
-
|
|
79
|
-
const patientName = patient && getPatientName(patient);
|
|
80
|
-
return patient ? (
|
|
81
|
-
<div className={styles.patientSearchContainer}>
|
|
82
|
-
<AddPatientToQueueContext.Provider value={{ currentServiceQueueUuid }}>
|
|
83
|
-
<div className={styles.patientBannerContainer}>
|
|
84
|
-
<div className={styles.patientBanner}>
|
|
85
|
-
<div className={styles.patientPhoto}>
|
|
86
|
-
<PatientPhoto patientUuid={patient.id} patientName={patientName} />
|
|
87
|
-
</div>
|
|
88
|
-
<PatientBannerPatientInfo patient={patient} />
|
|
89
|
-
<PatientBannerToggleContactDetailsButton
|
|
90
|
-
showContactDetails={showContactDetails}
|
|
91
|
-
toggleContactDetails={() => setContactDetails(!showContactDetails)}
|
|
92
|
-
/>
|
|
93
|
-
</div>
|
|
94
|
-
{showContactDetails ? (
|
|
95
|
-
<PatientBannerContactDetails patientId={patient.id} deceased={patient.deceasedBoolean} />
|
|
96
|
-
) : null}
|
|
97
|
-
</div>
|
|
98
|
-
<div className={styles.backButton}>
|
|
99
|
-
<Button
|
|
100
|
-
kind="ghost"
|
|
101
|
-
renderIcon={(props) => <ArrowLeftIcon size={24} {...props} />}
|
|
102
|
-
iconDescription={backButtonDescription}
|
|
103
|
-
size="sm"
|
|
104
|
-
onClick={handleBackToAction}>
|
|
105
|
-
<span>{backButtonDescription}</span>
|
|
106
|
-
</Button>
|
|
107
|
-
</div>
|
|
108
|
-
{activeVisit ? (
|
|
109
|
-
<ExistingVisitFormComponent visit={activeVisit} closeWorkspace={closeWorkspace} />
|
|
110
|
-
) : (
|
|
111
|
-
<>
|
|
112
|
-
{error ? (
|
|
113
|
-
<ErrorState headerTitle={t('errorFetchingAppointments', 'Error fetching appointments')} error={error} />
|
|
114
|
-
) : null}
|
|
115
|
-
|
|
116
|
-
{isLoading && !error ? (
|
|
117
|
-
<DataTableSkeleton role="progressbar" />
|
|
118
|
-
) : searchType === SearchTypes.SCHEDULED_VISITS && hasAppointments ? (
|
|
119
|
-
<PatientScheduledVisits
|
|
120
|
-
appointments={appointments}
|
|
121
|
-
patientUuid={selectedPatientUuid}
|
|
122
|
-
toggleSearchType={toggleSearchType}
|
|
123
|
-
closeWorkspace={closeWorkspace}
|
|
124
|
-
/>
|
|
125
|
-
) : searchType === SearchTypes.VISIT_FORM ? (
|
|
126
|
-
<VisitForm patientUuid={selectedPatientUuid} closeWorkspace={closeWorkspace} />
|
|
127
|
-
) : null}
|
|
128
|
-
</>
|
|
129
|
-
)}
|
|
130
|
-
</AddPatientToQueueContext.Provider>
|
|
131
|
-
</div>
|
|
132
|
-
) : null;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
export default PatientSearch;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export default function SearchIllustration({ width = '62', height = '48' }) {
|
|
4
|
-
return (
|
|
5
|
-
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
6
|
-
<defs>
|
|
7
|
-
<path
|
|
8
|
-
d="M36.001 3.218C47.482 9.838 51.403 24.52 44.782 36 38.162 47.482 23.48 51.403 12 44.782.518 38.162-3.403 23.48 3.218 12 9.838.518 24.52-3.403 36 3.218z"
|
|
9
|
-
id="a"
|
|
10
|
-
/>
|
|
11
|
-
</defs>
|
|
12
|
-
<g fill="none" fillRule="evenodd">
|
|
13
|
-
<path d="M36.001 3.218C47.482 9.838 51.403 24.52 44.782 36 38.162 47.482 23.48 51.403 12 44.782.518 38.162-3.403 23.48 3.218 12 9.838.518 24.52-3.403 36 3.218z" />
|
|
14
|
-
<mask id="b" fill="#fff">
|
|
15
|
-
<use xlinkHref="#a" />
|
|
16
|
-
</mask>
|
|
17
|
-
<use fill="#CEE6E5" xlinkHref="#a" />
|
|
18
|
-
<path
|
|
19
|
-
d="M38 48v-2.618c0-6.696-4.58-12.296-10.72-13.798a8.159 8.159 0 0 0 4.76-7.427C32.04 19.653 28.44 16 24 16c-4.44 0-8.04 3.653-8.04 8.157a8.159 8.159 0 0 0 4.76 7.427C14.58 33.086 10 38.686 10 45.382v2.598"
|
|
20
|
-
fill="#9ACBCA"
|
|
21
|
-
mask="url(#b)"
|
|
22
|
-
/>
|
|
23
|
-
<path fill="#9ACBCA" d="m45.34 35 17.124 10.33-1.55 2.57L43.79 37.568z" />
|
|
24
|
-
</g>
|
|
25
|
-
</svg>
|
|
26
|
-
);
|
|
27
|
-
}
|