@kenyaemr/esm-appointments-app 8.0.1-pre.99 → 8.0.3-pre.131
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 +25 -26
- package/dist/130.js +1 -1
- package/dist/130.js.LICENSE.txt +2 -0
- package/dist/130.js.map +1 -1
- package/dist/171.js +1 -0
- package/dist/171.js.map +1 -0
- package/dist/198.js +2 -0
- package/dist/198.js.map +1 -0
- package/dist/2.js +1 -0
- package/dist/2.js.map +1 -0
- package/dist/269.js +1 -0
- package/dist/269.js.map +1 -0
- package/dist/271.js +1 -1
- package/dist/319.js +1 -1
- package/dist/325.js +1 -0
- package/dist/325.js.map +1 -0
- package/dist/372.js +2 -0
- package/dist/372.js.map +1 -0
- package/dist/440.js +2 -0
- package/dist/440.js.LICENSE.txt +15 -0
- package/dist/440.js.map +1 -0
- package/dist/460.js +1 -1
- package/dist/529.js +1 -1
- package/dist/529.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/581.js +1 -1
- package/dist/644.js +1 -1
- package/dist/711.js +1 -0
- package/dist/711.js.map +1 -0
- package/dist/757.js +1 -1
- package/dist/787.js +1 -0
- package/dist/787.js.map +1 -0
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/kenyaemr-esm-appointments-app.js +1 -1
- package/dist/kenyaemr-esm-appointments-app.js.buildmanifest.json +203 -182
- package/dist/kenyaemr-esm-appointments-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +3 -3
- package/src/appointments/appointment-tabs.component.tsx +1 -2
- package/src/appointments/common-components/appointments-table.component.tsx +2 -2
- package/src/appointments/common-components/appointments-table.test.tsx +3 -3
- package/src/appointments/common-components/end-appointment.modal.tsx +24 -27
- package/src/appointments/common-components/end-appointment.test.tsx +10 -4
- package/src/appointments/unscheduled/unscheduled-appointments.component.tsx +2 -2
- package/src/appointments/unscheduled/unscheduled-appointments.test.tsx +3 -3
- package/src/appointments.component.tsx +1 -1
- package/src/appointments.test.tsx +5 -5
- package/src/calendar/appointments-calendar-view.component.tsx +1 -1
- package/src/calendar/header/calendar-header.component.tsx +7 -5
- package/src/calendar/header/calendar-header.scss +12 -0
- package/src/calendar/monthly/days-of-week.component.tsx +6 -4
- package/src/calendar/monthly/days-of-week.scss +5 -0
- package/src/calendar/monthly/monthly-calendar-view.component.tsx +2 -2
- package/src/calendar/monthly/monthly-header.component.tsx +49 -0
- package/src/calendar/monthly/monthly-view-workload.scss +0 -4
- package/src/calendar/monthly/monthly-workload-view-expanded.component.tsx +6 -1
- package/src/calendar/monthly/monthly-workload-view.component.tsx +3 -3
- package/src/config-schema.ts +6 -0
- package/src/constants.ts +1 -1
- package/src/form/appointments-form.component.tsx +59 -30
- package/src/form/appointments-form.scss +9 -0
- package/src/header/appointments-header.component.tsx +38 -55
- package/src/header/appointments-header.scss +7 -56
- package/src/helpers/excel.ts +31 -15
- package/src/hooks/useClinicalMetrics.ts +7 -5
- package/src/index.ts +15 -13
- package/src/metrics/metrics-header.component.tsx +3 -4
- package/src/routes.json +14 -18
- package/src/types/index.ts +0 -1
- package/translations/am.json +7 -2
- package/translations/ar.json +7 -2
- package/translations/en.json +8 -3
- package/translations/es.json +69 -64
- package/translations/fr.json +71 -66
- package/translations/he.json +7 -2
- package/translations/km.json +7 -2
- package/translations/zh.json +7 -2
- package/translations/zh_CN.json +7 -2
- package/dist/152.js +0 -1
- package/dist/152.js.map +0 -1
- package/dist/23.js +0 -2
- package/dist/23.js.LICENSE.txt +0 -5
- package/dist/23.js.map +0 -1
- package/dist/255.js +0 -2
- package/dist/255.js.map +0 -1
- package/dist/303.js +0 -1
- package/dist/303.js.map +0 -1
- package/dist/646.js +0 -2
- package/dist/646.js.map +0 -1
- package/dist/729.js +0 -1
- package/dist/729.js.map +0 -1
- package/dist/85.js +0 -1
- package/dist/85.js.map +0 -1
- package/dist/89.js +0 -1
- package/dist/89.js.map +0 -1
- package/src/calendar/monthly/monthly-header.module.tsx +0 -40
- package/src/header/appointments-illustration.component.tsx +0 -22
- /package/dist/{646.js.LICENSE.txt → 198.js.LICENSE.txt} +0 -0
- /package/dist/{255.js.LICENSE.txt → 372.js.LICENSE.txt} +0 -0
- /package/src/calendar/monthly/{monthly-header.module.scss → monthly-header.scss} +0 -0
|
@@ -176,7 +176,34 @@ const AppointmentsForm: React.FC<AppointmentsFormProps> = ({
|
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
178
|
path: ['appointmentDateTime.recurringPatternEndDate'],
|
|
179
|
-
message: 'A recurring appointment should have an end date',
|
|
179
|
+
message: t('recurringAppointmentShouldHaveEndDate', 'A recurring appointment should have an end date'),
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
.refine(
|
|
183
|
+
(formValues) => {
|
|
184
|
+
const { appointmentDateTime, dateAppointmentScheduled } = formValues;
|
|
185
|
+
|
|
186
|
+
const startDate = appointmentDateTime?.startDate;
|
|
187
|
+
|
|
188
|
+
if (!startDate || !dateAppointmentScheduled) return true;
|
|
189
|
+
|
|
190
|
+
const normalizeDate = (date: Date) => {
|
|
191
|
+
const normalizedDate = new Date(date);
|
|
192
|
+
normalizedDate.setHours(0, 0, 0, 0);
|
|
193
|
+
return normalizedDate;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const startDateObj = normalizeDate(startDate);
|
|
197
|
+
const scheduledDateObj = normalizeDate(dateAppointmentScheduled);
|
|
198
|
+
|
|
199
|
+
return scheduledDateObj <= startDateObj;
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
path: ['dateAppointmentScheduled'],
|
|
203
|
+
message: t(
|
|
204
|
+
'dateAppointmentIssuedCannotBeAfterAppointmentDate',
|
|
205
|
+
'Date appointment issued cannot be after the appointment date',
|
|
206
|
+
),
|
|
180
207
|
},
|
|
181
208
|
);
|
|
182
209
|
|
|
@@ -463,33 +490,6 @@ const AppointmentsForm: React.FC<AppointmentsFormProps> = ({
|
|
|
463
490
|
/>
|
|
464
491
|
</ResponsiveWrapper>
|
|
465
492
|
</section>
|
|
466
|
-
<section className={styles.formGroup}>
|
|
467
|
-
<span className={styles.heading}>{t('dateScheduled', 'Date appointment issued')}</span>
|
|
468
|
-
<ResponsiveWrapper>
|
|
469
|
-
<Controller
|
|
470
|
-
name="dateAppointmentScheduled"
|
|
471
|
-
control={control}
|
|
472
|
-
render={({ field: { onChange, value, ref }, fieldState }) => (
|
|
473
|
-
<DatePicker
|
|
474
|
-
datePickerType="single"
|
|
475
|
-
dateFormat={datePickerFormat}
|
|
476
|
-
value={value}
|
|
477
|
-
maxDate={new Date()}
|
|
478
|
-
onChange={([date]) => onChange(date)}
|
|
479
|
-
invalid={!!fieldState?.error?.message}
|
|
480
|
-
invalidText={fieldState?.error?.message}>
|
|
481
|
-
<DatePickerInput
|
|
482
|
-
id="dateAppointmentScheduledPickerInput"
|
|
483
|
-
labelText={t('dateScheduledDetail', 'Date appointment issued')}
|
|
484
|
-
style={{ width: '100%' }}
|
|
485
|
-
placeholder={datePickerPlaceHolder}
|
|
486
|
-
ref={ref}
|
|
487
|
-
/>
|
|
488
|
-
</DatePicker>
|
|
489
|
-
)}
|
|
490
|
-
/>
|
|
491
|
-
</ResponsiveWrapper>
|
|
492
|
-
</section>
|
|
493
493
|
<section className={styles.formGroup}>
|
|
494
494
|
<span className={styles.heading}>{t('service', 'Service')}</span>
|
|
495
495
|
<ResponsiveWrapper>
|
|
@@ -535,7 +535,6 @@ const AppointmentsForm: React.FC<AppointmentsFormProps> = ({
|
|
|
535
535
|
/>
|
|
536
536
|
</ResponsiveWrapper>
|
|
537
537
|
</section>
|
|
538
|
-
|
|
539
538
|
<section className={styles.formGroup}>
|
|
540
539
|
<span className={styles.heading}>{t('appointmentType_title', 'Appointment Type')}</span>
|
|
541
540
|
<ResponsiveWrapper>
|
|
@@ -725,7 +724,7 @@ const AppointmentsForm: React.FC<AppointmentsFormProps> = ({
|
|
|
725
724
|
<DatePicker
|
|
726
725
|
datePickerType="single"
|
|
727
726
|
dateFormat={datePickerFormat}
|
|
728
|
-
value={value
|
|
727
|
+
value={value?.startDate}
|
|
729
728
|
onChange={([date]) => {
|
|
730
729
|
if (date) {
|
|
731
730
|
onChange({ ...value, startDate: date });
|
|
@@ -821,6 +820,36 @@ const AppointmentsForm: React.FC<AppointmentsFormProps> = ({
|
|
|
821
820
|
/>
|
|
822
821
|
</ResponsiveWrapper>
|
|
823
822
|
</section>
|
|
823
|
+
<section className={styles.formGroup}>
|
|
824
|
+
<span className={styles.heading}>{t('dateScheduled', 'Date appointment issued')}</span>
|
|
825
|
+
<ResponsiveWrapper>
|
|
826
|
+
<Controller
|
|
827
|
+
name="dateAppointmentScheduled"
|
|
828
|
+
control={control}
|
|
829
|
+
render={({ field: { onChange, value, ref }, fieldState }) => (
|
|
830
|
+
<div style={{ width: '100%' }}>
|
|
831
|
+
<DatePicker
|
|
832
|
+
datePickerType="single"
|
|
833
|
+
dateFormat={datePickerFormat}
|
|
834
|
+
value={value}
|
|
835
|
+
maxDate={new Date().toISOString()}
|
|
836
|
+
onChange={([date]) => onChange(date)}
|
|
837
|
+
invalid={!!fieldState?.error?.message}
|
|
838
|
+
invalidText={fieldState?.error?.message}>
|
|
839
|
+
<DatePickerInput
|
|
840
|
+
id="dateAppointmentScheduledPickerInput"
|
|
841
|
+
labelText={t('dateScheduledDetail', 'Date appointment issued')}
|
|
842
|
+
style={{ width: '100%' }}
|
|
843
|
+
placeholder={datePickerPlaceHolder}
|
|
844
|
+
ref={ref}
|
|
845
|
+
/>
|
|
846
|
+
</DatePicker>
|
|
847
|
+
{fieldState?.error?.message && <div className={styles.errorMessage}>{fieldState.error.message}</div>}
|
|
848
|
+
</div>
|
|
849
|
+
)}
|
|
850
|
+
/>
|
|
851
|
+
</ResponsiveWrapper>
|
|
852
|
+
</section>
|
|
824
853
|
|
|
825
854
|
<section className={styles.formGroup}>
|
|
826
855
|
<span className={styles.heading}>{t('note', 'Note')}</span>
|
|
@@ -76,3 +76,12 @@ $openmrs-background-grey: #ededed;
|
|
|
76
76
|
max-inline-size: fit-content;
|
|
77
77
|
padding-top: layout.$spacing-03;
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
.errorMessage {
|
|
81
|
+
color: red;
|
|
82
|
+
font-size: 0.75rem;
|
|
83
|
+
margin-top: 0.25rem;
|
|
84
|
+
word-wrap: break-word;
|
|
85
|
+
white-space: pre-wrap;
|
|
86
|
+
text-align: left;
|
|
87
|
+
}
|
|
@@ -2,13 +2,11 @@ import React, { useContext } from 'react';
|
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { DatePicker, DatePickerInput, Dropdown } from '@carbon/react';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { PageHeader, PageHeaderContent, AppointmentsPictogram } from '@openmrs/esm-framework';
|
|
6
|
+
import { omrsDateFormat } from '../constants';
|
|
7
7
|
import { useAppointmentServices } from '../hooks/useAppointmentService';
|
|
8
|
-
import AppointmentsIllustration from './appointments-illustration.component';
|
|
9
|
-
import styles from './appointments-header.scss';
|
|
10
8
|
import SelectedDateContext from '../hooks/selectedDateContext';
|
|
11
|
-
import
|
|
9
|
+
import styles from './appointments-header.scss';
|
|
12
10
|
|
|
13
11
|
interface AppointmentHeaderProps {
|
|
14
12
|
title: string;
|
|
@@ -18,61 +16,46 @@ interface AppointmentHeaderProps {
|
|
|
18
16
|
|
|
19
17
|
const AppointmentsHeader: React.FC<AppointmentHeaderProps> = ({ title, appointmentServiceType, onChange }) => {
|
|
20
18
|
const { t } = useTranslation();
|
|
21
|
-
const session = useSession();
|
|
22
19
|
const { selectedDate, setSelectedDate } = useContext(SelectedDateContext);
|
|
23
|
-
const location = session?.sessionLocation?.display;
|
|
24
20
|
const { serviceTypes } = useAppointmentServices();
|
|
25
21
|
|
|
26
22
|
return (
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
serviceTypes.find((service) => service.uuid === appointmentServiceType) || { name: 'All', uuid: '' }
|
|
62
|
-
}
|
|
63
|
-
items={[{ name: 'All', uuid: '' }, ...serviceTypes]}
|
|
64
|
-
itemToString={(item) => (item ? item.name : '')}
|
|
65
|
-
label={t('selectServiceType', 'Select service type')}
|
|
66
|
-
type="inline"
|
|
67
|
-
size="sm"
|
|
68
|
-
direction="bottom"
|
|
69
|
-
titleText={t('view', 'View')}
|
|
70
|
-
onChange={({ selectedItem }) => onChange(selectedItem?.uuid)}
|
|
71
|
-
/>
|
|
72
|
-
)}
|
|
73
|
-
</div>
|
|
23
|
+
<PageHeader className={styles.header} data-testid="appointments-header">
|
|
24
|
+
<PageHeaderContent illustration={<AppointmentsPictogram />} title={title} />
|
|
25
|
+
<div className={styles.rightJustifiedItems}>
|
|
26
|
+
<DatePicker
|
|
27
|
+
dateFormat="d-M-Y"
|
|
28
|
+
datePickerType="single"
|
|
29
|
+
onChange={([date]) => setSelectedDate(dayjs(date).startOf('day').format(omrsDateFormat))}
|
|
30
|
+
value={dayjs(selectedDate).format('DD MMM YYYY')}>
|
|
31
|
+
<DatePickerInput
|
|
32
|
+
style={{ cursor: 'pointer', backgroundColor: 'transparent', border: 'none', maxWidth: '10rem' }}
|
|
33
|
+
id="appointment-date-picker"
|
|
34
|
+
labelText=""
|
|
35
|
+
placeholder="DD-MMM-YYYY"
|
|
36
|
+
type="text"
|
|
37
|
+
/>
|
|
38
|
+
</DatePicker>
|
|
39
|
+
{typeof onChange === 'function' && (
|
|
40
|
+
<Dropdown
|
|
41
|
+
aria-label={t('selectServiceType', 'Select service type')}
|
|
42
|
+
className={styles.dropdown}
|
|
43
|
+
direction="bottom"
|
|
44
|
+
id="serviceDropdown"
|
|
45
|
+
items={[{ name: 'All', uuid: '' }, ...serviceTypes]}
|
|
46
|
+
itemToString={(item) => (item ? item.name : '')}
|
|
47
|
+
label={t('selectServiceType', 'Select service type')}
|
|
48
|
+
onChange={({ selectedItem }) => onChange(selectedItem?.uuid)}
|
|
49
|
+
selectedItem={
|
|
50
|
+
serviceTypes.find((service) => service.uuid === appointmentServiceType) || { name: 'All', uuid: '' }
|
|
51
|
+
}
|
|
52
|
+
size="sm"
|
|
53
|
+
titleText={t('view', 'View')}
|
|
54
|
+
type="inline"
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
74
57
|
</div>
|
|
75
|
-
</
|
|
58
|
+
</PageHeader>
|
|
76
59
|
);
|
|
77
60
|
};
|
|
78
61
|
|
|
@@ -4,71 +4,22 @@
|
|
|
4
4
|
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
5
5
|
|
|
6
6
|
.header {
|
|
7
|
-
@include type.type-style('body-compact-02');
|
|
8
|
-
color: $text-02;
|
|
9
|
-
height: layout.$spacing-12;
|
|
10
7
|
background-color: $ui-02;
|
|
11
|
-
border
|
|
8
|
+
border: 1px solid $ui-03;
|
|
9
|
+
border-left: 0;
|
|
12
10
|
display: flex;
|
|
13
11
|
justify-content: space-between;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.left-justified-items {
|
|
17
|
-
display: flex;
|
|
18
|
-
flex-direction: row;
|
|
19
|
-
align-items: center;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.right-justified-items {
|
|
23
|
-
@include type.type-style('body-compact-02');
|
|
24
|
-
color: $text-02;
|
|
25
|
-
margin: layout.$spacing-03;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.page-name {
|
|
29
|
-
@include type.type-style('heading-04');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.page-labels {
|
|
33
|
-
margin: layout.$spacing-05 0;
|
|
34
|
-
|
|
35
|
-
p:first-of-type {
|
|
36
|
-
margin-bottom: layout.$spacing-02;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.date-and-location {
|
|
41
|
-
display: flex;
|
|
42
|
-
justify-content: flex-end;
|
|
43
12
|
align-items: center;
|
|
13
|
+
padding-right: layout.$spacing-03;
|
|
44
14
|
}
|
|
45
15
|
|
|
46
|
-
.
|
|
16
|
+
.rightJustifiedItems {
|
|
47
17
|
display: flex;
|
|
48
|
-
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
align-items: flex-end;
|
|
49
20
|
justify-content: flex-end;
|
|
50
|
-
margin-top: layout.$spacing-02;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.value {
|
|
54
|
-
margin-left: layout.$spacing-02;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.middot {
|
|
58
21
|
margin: 0 layout.$spacing-03;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.view {
|
|
62
|
-
@include type.type-style('label-01');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.datePicker {
|
|
66
|
-
background-color: transparent;
|
|
67
|
-
width: layout.$spacing-13;
|
|
68
|
-
border: none;
|
|
69
|
-
& > input {
|
|
70
|
-
color: colors.$blue-10;
|
|
71
|
-
}
|
|
22
|
+
row-gap: layout.$spacing-01;
|
|
72
23
|
}
|
|
73
24
|
|
|
74
25
|
.dropdown {
|
package/src/helpers/excel.ts
CHANGED
|
@@ -1,21 +1,37 @@
|
|
|
1
|
-
import { formatDate } from '@openmrs/esm-framework';
|
|
2
1
|
import * as XLSX from 'xlsx';
|
|
2
|
+
import { fetchCurrentPatient, formatDate, getConfig } from '@openmrs/esm-framework';
|
|
3
3
|
import { type Appointment } from '../types';
|
|
4
|
+
import { type ConfigObject } from '../config-schema';
|
|
5
|
+
import { moduleName } from '../constants';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param {Array<Appointment>} appointments - The list of appointments to
|
|
8
|
+
* Exports the provided appointments as an Excel spreadsheet.
|
|
9
|
+
* @param {Array<Appointment>} appointments - The list of appointments to export.
|
|
8
10
|
* @param {string} [fileName] - The name of the downloaded file
|
|
9
11
|
*/
|
|
10
|
-
export function
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
export async function exportAppointmentsToSpreadsheet(appointments: Array<Appointment>, fileName = 'Appointments') {
|
|
13
|
+
const config = await getConfig<ConfigObject>(moduleName);
|
|
14
|
+
const includePhoneNumbers = config.includePhoneNumberInExcelSpreadsheet ?? false;
|
|
15
|
+
|
|
16
|
+
const appointmentsJSON = await Promise.all(
|
|
17
|
+
appointments.map(async (appointment: Appointment) => {
|
|
18
|
+
const patientInfo = await fetchCurrentPatient(appointment.patient.uuid);
|
|
19
|
+
const phoneNumber =
|
|
20
|
+
includePhoneNumbers && patientInfo?.telecom
|
|
21
|
+
? patientInfo.telecom.map((telecomObj) => telecomObj?.value).join(', ')
|
|
22
|
+
: '';
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
'Patient name': appointment.patient.name,
|
|
26
|
+
Gender: appointment.patient.gender === 'F' ? 'Female' : 'Male',
|
|
27
|
+
Age: appointment.patient.age,
|
|
28
|
+
Identifier: appointment.patient.identifier ?? '--',
|
|
29
|
+
'Appointment type': appointment.service?.name,
|
|
30
|
+
Date: formatDate(new Date(appointment.startDateTime), { mode: 'wide' }),
|
|
31
|
+
...(includePhoneNumbers ? { 'Telephone number': phoneNumber } : {}),
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
19
35
|
|
|
20
36
|
const worksheet = createWorksheet(appointmentsJSON);
|
|
21
37
|
const workbook = createWorkbook(worksheet, 'Appointment list');
|
|
@@ -23,11 +39,11 @@ export function downloadAppointmentsAsExcel(appointments: Array<Appointment>, fi
|
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
/**
|
|
26
|
-
|
|
27
|
-
@param {Array<Object>} unscheduledAppointments - The list of unscheduled appointments to
|
|
42
|
+
Exports unscheduled appointments as an Excel spreadsheet.
|
|
43
|
+
@param {Array<Object>} unscheduledAppointments - The list of unscheduled appointments to export.
|
|
28
44
|
@param {string} fileName - The name of the file to download. Defaults to 'Unscheduled appointments {current date and time}'.
|
|
29
45
|
*/
|
|
30
|
-
export function
|
|
46
|
+
export function exportUnscheduledAppointmentsToSpreadsheet(
|
|
31
47
|
unscheduledAppointments: Array<any>,
|
|
32
48
|
fileName = `Unscheduled appointments ${formatDate(new Date(), { year: true, time: true })}`,
|
|
33
49
|
) {
|
|
@@ -45,11 +45,13 @@ export function useAllAppointmentsByDate() {
|
|
|
45
45
|
openmrsFetch,
|
|
46
46
|
);
|
|
47
47
|
|
|
48
|
-
const providersArray = data?.data?.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
).
|
|
48
|
+
const providersArray = data?.data?.flatMap(({ providers }) => providers ?? []) ?? [];
|
|
49
|
+
|
|
50
|
+
const validProviders = providersArray.filter((provider) => provider.response === 'ACCEPTED');
|
|
51
|
+
|
|
52
|
+
const uniqueProviders = uniqBy(validProviders, (provider) => provider.uuid);
|
|
53
|
+
const providersCount = uniqueProviders.length;
|
|
54
|
+
|
|
53
55
|
return {
|
|
54
56
|
totalProviders: providersCount ? providersCount : 0,
|
|
55
57
|
isLoading,
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
} from '@openmrs/esm-framework';
|
|
8
8
|
import { configSchema } from './config-schema';
|
|
9
9
|
import { createDashboardLink } from './createDashboardLink.component';
|
|
10
|
-
import { createDashboardLink as createPatientChartDashboardLink } from '@openmrs/esm-patient-common-lib';
|
|
11
10
|
import { dashboardMeta, appointmentCalendarDashboardMeta, patientChartDashboardMeta } from './dashboard.meta';
|
|
12
11
|
import {
|
|
13
12
|
cancelledAppointmentsPanelConfigSchema,
|
|
@@ -22,10 +21,7 @@ import appointmentsDashboardComponent from './appointments.component';
|
|
|
22
21
|
import homeAppointmentsComponent from './home/home-appointments.component';
|
|
23
22
|
import appointmentsListComponent from './appointments/scheduled/appointments-list.component';
|
|
24
23
|
import earlyAppointmentsComponent from './appointments/scheduled/early-appointments.component';
|
|
25
|
-
import
|
|
26
|
-
import patientAppointmentsOverviewComponent from './patient-appointments/patient-appointments-overview.component';
|
|
27
|
-
import patientUpcomingAppointmentsComponent from './patient-appointments/patient-upcoming-appointments-card.component';
|
|
28
|
-
import appointementsForm from './form/appointments-form.component';
|
|
24
|
+
import appointmentsFormComponent from './form/appointments-form.component';
|
|
29
25
|
import patientSearch from './patient-search/patient-search.component';
|
|
30
26
|
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
31
27
|
|
|
@@ -79,24 +75,30 @@ export const appointmentsList = getSyncLifecycle(appointmentsListComponent, opti
|
|
|
79
75
|
|
|
80
76
|
export const earlyAppointments = getSyncLifecycle(earlyAppointmentsComponent, options);
|
|
81
77
|
|
|
82
|
-
export const
|
|
78
|
+
export const appointmentsForm = getSyncLifecycle(appointmentsFormComponent, options);
|
|
83
79
|
|
|
84
80
|
export const searchPatient = getSyncLifecycle(patientSearch, options);
|
|
85
81
|
|
|
86
82
|
// t('Appointments', 'Appointments')
|
|
87
|
-
export const patientAppointmentsSummaryDashboardLink =
|
|
88
|
-
|
|
83
|
+
export const patientAppointmentsSummaryDashboardLink = getAsyncLifecycle(async () => {
|
|
84
|
+
const commonLib = await import('@openmrs/esm-patient-common-lib');
|
|
85
|
+
return { default: commonLib.createDashboardLink({ ...patientChartDashboardMeta, moduleName }) };
|
|
86
|
+
}, options);
|
|
87
|
+
|
|
88
|
+
export const patientAppointmentsDetailedSummary = getAsyncLifecycle(
|
|
89
|
+
() => import('./patient-appointments/patient-appointments-detailed-summary.component'),
|
|
89
90
|
options,
|
|
90
91
|
);
|
|
91
92
|
|
|
92
|
-
export const
|
|
93
|
-
|
|
93
|
+
export const patientAppointmentsOverview = getAsyncLifecycle(
|
|
94
|
+
() => import('./patient-appointments/patient-appointments-overview.component'),
|
|
94
95
|
options,
|
|
95
96
|
);
|
|
96
97
|
|
|
97
|
-
export const
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
export const patientUpcomingAppointmentsWidget = getAsyncLifecycle(
|
|
99
|
+
() => import('./patient-appointments/patient-upcoming-appointments-card.component'),
|
|
100
|
+
options,
|
|
101
|
+
);
|
|
100
102
|
|
|
101
103
|
export const patientAppointmentsCancelConfirmationDialog = getAsyncLifecycle(
|
|
102
104
|
() => import('./patient-appointments/patient-appointments-cancel.modal'),
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import isToday from 'dayjs/plugin/isToday';
|
|
4
|
-
import { launchWorkspace } from '@openmrs/esm-framework';
|
|
5
4
|
import { useTranslation } from 'react-i18next';
|
|
6
5
|
import { Calendar, Hospital } from '@carbon/react/icons';
|
|
7
6
|
import { Button } from '@carbon/react';
|
|
8
|
-
import { ExtensionSlot, isDesktop, navigate, useLayoutType } from '@openmrs/esm-framework';
|
|
7
|
+
import { ExtensionSlot, isDesktop, launchWorkspace, navigate, useLayoutType } from '@openmrs/esm-framework';
|
|
9
8
|
import { spaHomePage } from '../constants';
|
|
10
|
-
import styles from './metrics-header.scss';
|
|
11
9
|
import SelectedDateContext from '../hooks/selectedDateContext';
|
|
10
|
+
import styles from './metrics-header.scss';
|
|
12
11
|
|
|
13
12
|
dayjs.extend(isToday);
|
|
14
13
|
|
|
@@ -39,7 +38,7 @@ const MetricsHeader: React.FC = () => {
|
|
|
39
38
|
onClick={() =>
|
|
40
39
|
navigate({ to: `${spaHomePage}/appointments/calendar/${dayjs(selectedDate).format('YYYY-MM-DD')}` })
|
|
41
40
|
}>
|
|
42
|
-
{t('appointmentsCalendar', 'Appointments
|
|
41
|
+
{t('appointmentsCalendar', 'Appointments calendar')}
|
|
43
42
|
</Button>
|
|
44
43
|
<ExtensionSlot
|
|
45
44
|
name="patient-search-button-slot"
|
package/src/routes.json
CHANGED
|
@@ -3,12 +3,6 @@
|
|
|
3
3
|
"backendDependencies": {
|
|
4
4
|
"webservices.rest": "^2.2.0"
|
|
5
5
|
},
|
|
6
|
-
"modals": [
|
|
7
|
-
{
|
|
8
|
-
"name": "end-appointment-modal",
|
|
9
|
-
"component": "endAppointmentModal"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
6
|
"extensions": [
|
|
13
7
|
{
|
|
14
8
|
"name": "home-appointments",
|
|
@@ -36,11 +30,7 @@
|
|
|
36
30
|
"slot": "calendar-dashboard-slot",
|
|
37
31
|
"component": "appointmentsCalendarDashboardLink"
|
|
38
32
|
},
|
|
39
|
-
|
|
40
|
-
"name": "check-in-appointment-modal",
|
|
41
|
-
"slot": "todays-appointment-slot",
|
|
42
|
-
"component": "checkInModal"
|
|
43
|
-
},
|
|
33
|
+
|
|
44
34
|
{
|
|
45
35
|
"name": "todays-appointments-dashboard",
|
|
46
36
|
"slot": "todays-appointment-slot",
|
|
@@ -111,13 +101,9 @@
|
|
|
111
101
|
"component": "patientUpcomingAppointmentsWidget",
|
|
112
102
|
"slot": "upcoming-appointment-slot"
|
|
113
103
|
},
|
|
114
|
-
{
|
|
115
|
-
"name": "patient-appointment-cancel-confirmation-dialog",
|
|
116
|
-
"component": "patientAppointmentsCancelConfirmationDialog"
|
|
117
|
-
},
|
|
118
104
|
{
|
|
119
105
|
"name": "edit-appointments-form",
|
|
120
|
-
"component": "
|
|
106
|
+
"component": "appointmentsForm",
|
|
121
107
|
"meta": {
|
|
122
108
|
"title":{
|
|
123
109
|
"key":"editAppointment",
|
|
@@ -131,7 +117,7 @@
|
|
|
131
117
|
},
|
|
132
118
|
{
|
|
133
119
|
"name": "create-appointment",
|
|
134
|
-
"component": "
|
|
120
|
+
"component": "appointmentsForm",
|
|
135
121
|
"meta": {
|
|
136
122
|
"title": {
|
|
137
123
|
"key":"appointmentForm",
|
|
@@ -141,7 +127,7 @@
|
|
|
141
127
|
},
|
|
142
128
|
{
|
|
143
129
|
"name": "add-appointment",
|
|
144
|
-
"component": "
|
|
130
|
+
"component": "appointmentsForm",
|
|
145
131
|
"meta": {
|
|
146
132
|
"title": {
|
|
147
133
|
"key": "createNewAppointment",
|
|
@@ -154,5 +140,15 @@
|
|
|
154
140
|
"slot": "home-metrics-tiles-slot",
|
|
155
141
|
"component": "homeAppointmentsTile"
|
|
156
142
|
}
|
|
143
|
+
],
|
|
144
|
+
"modals": [
|
|
145
|
+
{
|
|
146
|
+
"name": "end-appointment-modal",
|
|
147
|
+
"component": "endAppointmentModal"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "patient-appointment-cancel-confirmation-dialog",
|
|
151
|
+
"component": "patientAppointmentsCancelConfirmationDialog"
|
|
152
|
+
}
|
|
157
153
|
]
|
|
158
154
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -27,7 +27,6 @@ export enum AppointmentKind {
|
|
|
27
27
|
WALKIN = 'WalkIn',
|
|
28
28
|
VIRTUAL = 'Virtual',
|
|
29
29
|
}
|
|
30
|
-
|
|
31
30
|
// TODO: remove interface elements that aren't actually present on the Appointment object returned from the Appointment API
|
|
32
31
|
export interface Appointment {
|
|
33
32
|
appointmentKind: AppointmentKind;
|
package/translations/am.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"appointments": "Appointments",
|
|
28
28
|
"Appointments": "Appointments",
|
|
29
29
|
"appointments_lower": "appointments",
|
|
30
|
-
"appointmentsCalendar": "Appointments
|
|
30
|
+
"appointmentsCalendar": "Appointments calendar",
|
|
31
31
|
"appointmentScheduled": "Appointment scheduled",
|
|
32
32
|
"appointmentService": "Appointment service",
|
|
33
33
|
"appointmentServiceCreate": "Appointment service created successfully",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"createNewAppointment": "Create new appointment",
|
|
63
63
|
"date": "Date",
|
|
64
64
|
"date&Time": "Date & time",
|
|
65
|
+
"dateAppointmentIssuedCannotBeAfterAppointmentDate": "Date appointment issued cannot be after the appointment date",
|
|
65
66
|
"dateOfBirth": "Date of birth",
|
|
66
67
|
"dateScheduled": "Date appointment issued",
|
|
67
68
|
"dateScheduledDetail": "Date appointment issued",
|
|
@@ -89,7 +90,6 @@
|
|
|
89
90
|
"filterTable": "Filter table",
|
|
90
91
|
"gender": "Gender",
|
|
91
92
|
"highestServiceVolume": "Highest volume service: {{time}}",
|
|
92
|
-
"home": "Home",
|
|
93
93
|
"identifier": "Identifier",
|
|
94
94
|
"invalidNumber": "Number is not valid",
|
|
95
95
|
"invalidTime": "Invalid time",
|
|
@@ -99,6 +99,8 @@
|
|
|
99
99
|
"location": "Location",
|
|
100
100
|
"medications": "Medications",
|
|
101
101
|
"missed": "Missed",
|
|
102
|
+
"next": "Next",
|
|
103
|
+
"nextMonth": "Next month",
|
|
102
104
|
"nextPage": "Next page",
|
|
103
105
|
"no": "No",
|
|
104
106
|
"noAppointmentsToDisplay": "No appointments to display",
|
|
@@ -119,11 +121,14 @@
|
|
|
119
121
|
"patientName": "Patient name",
|
|
120
122
|
"patients": "Patients",
|
|
121
123
|
"period": "Period",
|
|
124
|
+
"prev": "Prev",
|
|
125
|
+
"previousMonth": "Previous month",
|
|
122
126
|
"previousPage": "Previous page",
|
|
123
127
|
"provider": "Provider",
|
|
124
128
|
"providers": "Providers",
|
|
125
129
|
"providersBooked": "Providers booked: {{time}}",
|
|
126
130
|
"recurringAppointment": "Recurring Appointment",
|
|
131
|
+
"recurringAppointmentShouldHaveEndDate": "A recurring appointment should have an end date",
|
|
127
132
|
"repeatEvery": "Repeat every",
|
|
128
133
|
"save": "Save",
|
|
129
134
|
"saveAndClose": "Save and close",
|