@openmrs/esm-appointments-app 10.0.2 → 10.0.3-pre.2
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 +6 -5
- package/dist/1339.js +1 -0
- package/dist/1339.js.map +1 -0
- package/dist/1465.js +1 -0
- package/dist/1465.js.map +1 -0
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1789.js +1 -0
- package/dist/1789.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/1884.js +1 -0
- package/dist/1884.js.map +1 -0
- package/dist/1962.js +1 -0
- package/dist/1962.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2495.js +1 -0
- package/dist/2495.js.map +1 -0
- package/dist/2539.js +1 -0
- package/dist/2539.js.map +1 -0
- package/dist/2620.js +1 -0
- package/dist/2620.js.map +1 -0
- package/dist/2717.js +1 -0
- package/dist/2717.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/3198.js +11 -0
- package/dist/3198.js.map +1 -0
- package/dist/3220.js +1 -0
- package/dist/3220.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3720.js +1 -0
- package/dist/3720.js.map +1 -0
- package/dist/3930.js +1 -0
- package/dist/3930.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/3989.js +1 -0
- package/dist/3989.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/4307.js +1 -0
- package/dist/4307.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5640.js +1 -0
- package/dist/5640.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6098.js +38 -0
- package/dist/6098.js.map +1 -0
- package/dist/6236.js +1 -0
- package/dist/6236.js.map +1 -0
- package/dist/635.js +1 -0
- package/dist/635.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/6789.js +1 -0
- package/dist/6789.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7138.js +1 -0
- package/dist/7138.js.map +1 -0
- package/dist/7159.js +1 -0
- package/dist/7159.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7357.js +1 -0
- package/dist/7357.js.map +1 -0
- package/dist/7609.js +1 -0
- package/dist/7609.js.map +1 -0
- package/dist/7654.js +1 -0
- package/dist/7654.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8063.js +1 -0
- package/dist/8063.js.map +1 -0
- package/dist/8346.js +1 -0
- package/dist/8346.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/8937.js +1 -0
- package/dist/8937.js.map +1 -0
- package/dist/8981.js +1 -0
- package/dist/8981.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9061.js +1 -0
- package/dist/9061.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9282.js +1 -0
- package/dist/9282.js.map +1 -0
- package/dist/9399.js +1 -0
- package/dist/9399.js.map +1 -0
- package/dist/9443.js +1 -0
- package/dist/9443.js.map +1 -0
- package/dist/9581.js +1 -0
- package/dist/9581.js.map +1 -0
- package/dist/9712.js +1 -0
- package/dist/9712.js.map +1 -0
- package/dist/9771.js +1 -0
- package/dist/9771.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/main.js +6 -5
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-appointments-app.js +6 -5
- package/dist/openmrs-esm-appointments-app.js.buildmanifest.json +625 -620
- package/dist/openmrs-esm-appointments-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +3 -2
- package/src/appointments/appointment-tabs.component.tsx +38 -16
- package/src/appointments/common-components/appointments-actions.component.tsx +39 -47
- package/src/appointments/common-components/appointments-actions.test.tsx +52 -99
- package/src/appointments/common-components/appointments-table.component.tsx +109 -135
- package/src/appointments/common-components/appointments-table.scss +10 -6
- package/src/appointments/common-components/appointments-table.test.tsx +2 -9
- package/src/appointments/common-components/batch-change-appointment-statuses.modal.tsx +1 -1
- package/src/appointments/common-components/batch-change-appointment-statuses.test.tsx +8 -6
- package/src/appointments/common-components/checkin-button.component.tsx +60 -29
- package/src/appointments/common-components/end-appointment.modal.tsx +1 -1
- package/src/appointments/common-components/end-appointment.test.tsx +1 -1
- package/src/appointments/scheduled/early-appointments.component.tsx +6 -7
- package/src/appointments/scheduled/scheduled-appointments.component.tsx +26 -206
- package/src/appointments/utils.tsx +29 -16
- package/src/appointments.component.tsx +5 -13
- package/src/appointments.test.tsx +16 -4
- package/src/calendar/appointments-calendar-view.component.tsx +3 -12
- package/src/calendar/appointments-calendar-view.test.tsx +6 -1
- package/src/calendar/header/calendar-header.component.tsx +2 -2
- package/src/calendar/monthly/monthly-calendar-view.component.tsx +2 -2
- package/src/calendar/monthly/monthly-header.component.tsx +9 -8
- package/src/calendar/monthly/monthly-workload-view.component.tsx +2 -2
- package/src/config-schema.ts +17 -9
- package/src/constants.ts +12 -1
- package/src/dashboard.meta.ts +1 -1
- package/src/form/appointments-form.resource.ts +1 -120
- package/src/form/appointments-form.workspace.tsx +15 -18
- package/src/header/appointments-header.component.tsx +27 -9
- package/src/helpers/functions.ts +1 -50
- package/src/home/home-appointments.component.tsx +17 -4
- package/src/hooks/useActiveVisits.ts +14 -0
- package/src/hooks/useAppointmentList.ts +12 -29
- package/src/hooks/useAppointmentService.ts +6 -1
- package/src/hooks/useAppointmentsAppContext.ts +19 -0
- package/src/hooks/useAppointmentsCalendar.ts +2 -1
- package/src/hooks/useMutateAppointments.ts +24 -0
- package/src/hooks/usePatientAppointmentHistory.ts +3 -2
- package/src/hooks/useSelectedDate.ts +24 -0
- package/src/hooks/useUnscheduledAppointments.ts +5 -1
- package/src/index.ts +0 -21
- package/src/metrics/metrics-cards/highest-volume-service.extension.tsx +27 -6
- package/src/metrics/metrics-cards/metrics-card.component.tsx +2 -1
- package/src/metrics/metrics-cards/providers-booked.extension.tsx +14 -6
- package/src/metrics/metrics-cards/scheduled-appointments.extension.tsx +20 -16
- package/src/metrics/metrics-container.component.tsx +1 -5
- package/src/metrics/metrics-header.component.tsx +2 -2
- package/src/patient-appointments/patient-appointments-detailed-summary.extension.tsx +1 -1
- package/src/patient-appointments/patient-upcoming-appointments-card.component.tsx +3 -4
- package/src/routes.json +3 -27
- package/src/store.ts +24 -41
- package/src/types/index.ts +7 -0
- package/src/workload/monthly-view-workload/monthly-view.component.tsx +2 -2
- package/src/workload/workload.component.tsx +3 -3
- package/src/workload/workload.resource.ts +1 -1
- package/translations/am.json +15 -6
- package/translations/ar.json +15 -6
- package/translations/ar_SY.json +15 -6
- package/translations/bn.json +15 -6
- package/translations/cs.json +15 -6
- package/translations/de.json +177 -168
- package/translations/en.json +15 -6
- package/translations/en_US.json +15 -6
- package/translations/es.json +15 -6
- package/translations/es_MX.json +15 -6
- package/translations/fr.json +15 -6
- package/translations/he.json +15 -6
- package/translations/hi.json +15 -6
- package/translations/hi_IN.json +15 -6
- package/translations/id.json +15 -6
- package/translations/it.json +15 -6
- package/translations/ka.json +15 -6
- package/translations/km.json +15 -6
- package/translations/ku.json +15 -6
- package/translations/ky.json +15 -6
- package/translations/lg.json +15 -6
- package/translations/ne.json +15 -6
- package/translations/pl.json +15 -6
- package/translations/pt.json +15 -6
- package/translations/pt_BR.json +15 -6
- package/translations/qu.json +15 -6
- package/translations/ro_RO.json +15 -6
- package/translations/ru_RU.json +15 -6
- package/translations/si.json +15 -6
- package/translations/sq.json +15 -6
- package/translations/sw.json +15 -6
- package/translations/sw_KE.json +15 -6
- package/translations/tr.json +15 -6
- package/translations/tr_TR.json +15 -6
- package/translations/uk.json +15 -6
- package/translations/uz.json +15 -6
- package/translations/uz@Latn.json +15 -6
- package/translations/uz_UZ.json +15 -6
- package/translations/vi.json +15 -6
- package/translations/zh.json +63 -54
- package/translations/zh_CN.json +19 -10
- package/translations/zh_TW.json +15 -6
- package/dist/1187.js +0 -1
- package/dist/1187.js.map +0 -1
- package/dist/126.js +0 -1
- package/dist/1499.js +0 -1
- package/dist/1499.js.map +0 -1
- package/dist/15.js +0 -1
- package/dist/1522.js +0 -1
- package/dist/1522.js.map +0 -1
- package/dist/1564.js +0 -1
- package/dist/1567.js +0 -1
- package/dist/1777.js +0 -1
- package/dist/1777.js.map +0 -1
- package/dist/1845.js +0 -1
- package/dist/1899.js +0 -1
- package/dist/1899.js.map +0 -1
- package/dist/1953.js +0 -1
- package/dist/2056.js +0 -11
- package/dist/2056.js.map +0 -1
- package/dist/2104.js +0 -1
- package/dist/2104.js.map +0 -1
- package/dist/215.js +0 -1
- package/dist/2178.js +0 -1
- package/dist/2417.js +0 -1
- package/dist/2417.js.map +0 -1
- package/dist/2566.js +0 -1
- package/dist/2586.js +0 -1
- package/dist/2586.js.map +0 -1
- package/dist/2759.js +0 -1
- package/dist/276.js +0 -1
- package/dist/276.js.map +0 -1
- package/dist/3089.js +0 -1
- package/dist/3089.js.map +0 -1
- package/dist/3127.js +0 -1
- package/dist/3127.js.map +0 -1
- package/dist/3230.js +0 -1
- package/dist/3277.js +0 -1
- package/dist/3277.js.map +0 -1
- package/dist/3441.js +0 -1
- package/dist/3565.js +0 -1
- package/dist/3571.js +0 -1
- package/dist/3571.js.map +0 -1
- package/dist/3746.js +0 -1
- package/dist/3925.js +0 -1
- package/dist/3946.js +0 -1
- package/dist/4085.js +0 -1
- package/dist/4085.js.map +0 -1
- package/dist/4108.js +0 -1
- package/dist/4108.js.map +0 -1
- package/dist/4448.js +0 -1
- package/dist/4448.js.map +0 -1
- package/dist/4744.js +0 -1
- package/dist/4744.js.map +0 -1
- package/dist/4809.js +0 -1
- package/dist/486.js +0 -1
- package/dist/486.js.map +0 -1
- package/dist/4894.js +0 -1
- package/dist/4970.js +0 -1
- package/dist/4970.js.map +0 -1
- package/dist/5130.js +0 -1
- package/dist/5187.js +0 -1
- package/dist/5218.js +0 -1
- package/dist/5218.js.map +0 -1
- package/dist/5327.js +0 -1
- package/dist/5327.js.map +0 -1
- package/dist/5388.js +0 -1
- package/dist/5388.js.map +0 -1
- package/dist/5491.js +0 -1
- package/dist/5491.js.map +0 -1
- package/dist/5595.js +0 -1
- package/dist/5657.js +0 -38
- package/dist/5657.js.map +0 -1
- package/dist/5961.js +0 -1
- package/dist/6133.js +0 -1
- package/dist/634.js +0 -1
- package/dist/634.js.map +0 -1
- package/dist/6456.js +0 -1
- package/dist/6466.js +0 -1
- package/dist/6613.js +0 -1
- package/dist/6783.js +0 -1
- package/dist/703.js +0 -1
- package/dist/703.js.map +0 -1
- package/dist/7251.js +0 -1
- package/dist/7251.js.map +0 -1
- package/dist/7348.js +0 -1
- package/dist/7433.js +0 -1
- package/dist/7433.js.map +0 -1
- package/dist/7513.js +0 -1
- package/dist/7513.js.map +0 -1
- package/dist/7543.js +0 -1
- package/dist/7607.js +0 -1
- package/dist/772.js +0 -1
- package/dist/8139.js +0 -1
- package/dist/8139.js.map +0 -1
- package/dist/8456.js +0 -1
- package/dist/8456.js.map +0 -1
- package/dist/8588.js +0 -1
- package/dist/8588.js.map +0 -1
- package/dist/8599.js +0 -1
- package/dist/8727.js +0 -1
- package/dist/8847.js +0 -1
- package/dist/8919.js +0 -1
- package/dist/8919.js.map +0 -1
- package/dist/9015.js +0 -1
- package/dist/9051.js +0 -1
- package/dist/9051.js.map +0 -1
- package/dist/906.js +0 -1
- package/dist/9065.js +0 -1
- package/dist/9182.js +0 -1
- package/dist/9260.js +0 -1
- package/dist/9260.js.map +0 -1
- package/dist/9327.js +0 -1
- package/dist/9327.js.map +0 -1
- package/dist/9339.js +0 -1
- package/dist/9453.js +0 -1
- package/dist/9589.js +0 -1
- package/dist/9589.js.map +0 -1
- package/dist/9650.js +0 -1
- package/dist/9650.js.map +0 -1
- package/dist/9833.js +0 -1
- package/dist/9833.js.map +0 -1
- package/dist/9920.js +0 -1
- package/dist/9938.js +0 -1
- package/dist/9943.js +0 -1
- package/dist/9943.js.map +0 -1
- package/src/appointments/scheduled/appointments-list.component.tsx +0 -51
- package/src/hooks/useClinicalMetrics.ts +0 -94
- package/src/hooks/useTodaysVisits.ts +0 -19
- package/src/scheduled-appointments-config-schema.ts +0 -177
|
@@ -1,102 +1,9 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
2
1
|
import dayjs from 'dayjs';
|
|
3
2
|
import isToday from 'dayjs/plugin/isToday';
|
|
4
|
-
import useSWR, { useSWRConfig } from 'swr';
|
|
5
3
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
6
|
-
import {
|
|
7
|
-
type AppointmentPayload,
|
|
8
|
-
type AppointmentService,
|
|
9
|
-
type AppointmentsFetchResponse,
|
|
10
|
-
type RecurringAppointmentsPayload,
|
|
11
|
-
} from '../types';
|
|
4
|
+
import { type AppointmentPayload, type RecurringAppointmentsPayload } from '../types';
|
|
12
5
|
dayjs.extend(isToday);
|
|
13
6
|
|
|
14
|
-
const appointmentUrlMatcher = `${restBaseUrl}/appointment`;
|
|
15
|
-
const appointmentsSearchUrl = `${restBaseUrl}/appointments/search`;
|
|
16
|
-
|
|
17
|
-
export function useMutateAppointments() {
|
|
18
|
-
const { mutate } = useSWRConfig();
|
|
19
|
-
// this mutate is intentionally broad because there may be many different keys that need to be invalidated when appointments are updated
|
|
20
|
-
const mutateAppointments = useCallback(
|
|
21
|
-
() =>
|
|
22
|
-
mutate((key) => {
|
|
23
|
-
return (
|
|
24
|
-
(typeof key === 'string' && key.startsWith(appointmentUrlMatcher)) ||
|
|
25
|
-
(Array.isArray(key) && key[0].startsWith(appointmentUrlMatcher))
|
|
26
|
-
);
|
|
27
|
-
}),
|
|
28
|
-
[mutate],
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
mutateAppointments,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function useAppointments(patientUuid: string, startDate: string, abortController: AbortController) {
|
|
37
|
-
/*
|
|
38
|
-
SWR isn't meant to make POST requests for data fetching. This is a consequence of the API only exposing this resource via POST.
|
|
39
|
-
This works but likely isn't recommended.
|
|
40
|
-
*/
|
|
41
|
-
const fetcher = () =>
|
|
42
|
-
openmrsFetch(appointmentsSearchUrl, {
|
|
43
|
-
method: 'POST',
|
|
44
|
-
signal: abortController.signal,
|
|
45
|
-
headers: {
|
|
46
|
-
'Content-Type': 'application/json',
|
|
47
|
-
},
|
|
48
|
-
body: {
|
|
49
|
-
patientUuid: patientUuid,
|
|
50
|
-
startDate: startDate,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const { data, error, isLoading, isValidating, mutate } = useSWR<AppointmentsFetchResponse, Error>(
|
|
55
|
-
appointmentsSearchUrl,
|
|
56
|
-
fetcher,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
const appointments = data?.data?.length ? data.data : null;
|
|
60
|
-
|
|
61
|
-
const pastAppointments = appointments
|
|
62
|
-
?.sort((a, b) => (b.startDateTime > a.startDateTime ? 1 : -1))
|
|
63
|
-
?.filter(({ status }) => status !== 'Cancelled')
|
|
64
|
-
?.filter(({ startDateTime }) =>
|
|
65
|
-
dayjs(new Date(startDateTime).toISOString()).isBefore(new Date().setHours(0, 0, 0, 0)),
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const upcomingAppointments = appointments
|
|
69
|
-
?.sort((a, b) => (a.startDateTime > b.startDateTime ? 1 : -1))
|
|
70
|
-
?.filter(({ status }) => status !== 'Cancelled')
|
|
71
|
-
?.filter(({ startDateTime }) => dayjs(new Date(startDateTime).toISOString()).isAfter(new Date()));
|
|
72
|
-
|
|
73
|
-
const todaysAppointments = appointments
|
|
74
|
-
?.sort((a, b) => (a.startDateTime > b.startDateTime ? 1 : -1))
|
|
75
|
-
?.filter(({ status }) => status !== 'Cancelled')
|
|
76
|
-
?.filter(({ startDateTime }) => dayjs(new Date(startDateTime).toISOString()).isToday());
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
data: data ? { pastAppointments, upcomingAppointments, todaysAppointments } : null,
|
|
80
|
-
error,
|
|
81
|
-
isLoading,
|
|
82
|
-
isValidating,
|
|
83
|
-
mutate,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function useAppointmentService() {
|
|
88
|
-
const { data, error, isLoading } = useSWR<{ data: Array<AppointmentService> }, Error>(
|
|
89
|
-
`${restBaseUrl}/appointmentService/all/full`,
|
|
90
|
-
openmrsFetch,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
data: data ? data.data : null,
|
|
95
|
-
error,
|
|
96
|
-
isLoading,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
7
|
export function saveAppointment(appointment: AppointmentPayload, abortController: AbortController) {
|
|
101
8
|
return openmrsFetch(`${restBaseUrl}/appointment`, {
|
|
102
9
|
method: 'POST',
|
|
@@ -122,32 +29,6 @@ export function saveRecurringAppointments(
|
|
|
122
29
|
});
|
|
123
30
|
}
|
|
124
31
|
|
|
125
|
-
// TODO refactor to use SWR?
|
|
126
|
-
export function getAppointmentsByUuid(appointmentUuid: string, abortController: AbortController) {
|
|
127
|
-
return openmrsFetch(`${restBaseUrl}/appointments/${appointmentUuid}`, {
|
|
128
|
-
signal: abortController.signal,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// TODO refactor to use SWR?
|
|
133
|
-
export function getAppointmentService(abortController: AbortController, uuid) {
|
|
134
|
-
return openmrsFetch(`${restBaseUrl}/appointmentService?uuid=` + uuid, {
|
|
135
|
-
signal: abortController.signal,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export const cancelAppointment = async (toStatus: string, appointmentUuid: string) => {
|
|
140
|
-
const omrsDateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZZ';
|
|
141
|
-
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
142
|
-
const statusChangeTime = dayjs(new Date()).format(omrsDateFormat);
|
|
143
|
-
const url = `${restBaseUrl}/appointments/${appointmentUuid}/status-change`;
|
|
144
|
-
return await openmrsFetch(url, {
|
|
145
|
-
body: { toStatus, onDate: statusChangeTime, timeZone: timeZone },
|
|
146
|
-
method: 'POST',
|
|
147
|
-
headers: { 'Content-Type': 'application/json' },
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
|
-
|
|
151
32
|
export const checkAppointmentConflict = async (appointmentPayload: AppointmentPayload) => {
|
|
152
33
|
return await openmrsFetch(`${restBaseUrl}/appointments/conflicts`, {
|
|
153
34
|
method: 'POST',
|
|
@@ -40,17 +40,13 @@ import {
|
|
|
40
40
|
import { z } from 'zod';
|
|
41
41
|
import { type ConfigObject } from '../config-schema';
|
|
42
42
|
import type { Appointment, AppointmentPayload, RecurringPattern } from '../types';
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
saveAppointment,
|
|
46
|
-
saveRecurringAppointments,
|
|
47
|
-
useAppointmentService,
|
|
48
|
-
useMutateAppointments,
|
|
49
|
-
} from './appointments-form.resource';
|
|
43
|
+
import { checkAppointmentConflict, saveAppointment, saveRecurringAppointments } from './appointments-form.resource';
|
|
44
|
+
import { useAppointmentServices } from '../hooks/useAppointmentService';
|
|
50
45
|
import { appointmentLocationTagName, dateFormat, moduleName, weekDays } from '../constants';
|
|
51
|
-
import { useAppointmentsStore } from '../store';
|
|
52
46
|
import { useProviders } from '../hooks/useProviders';
|
|
47
|
+
import { useMutateAppointments } from '../hooks/useMutateAppointments';
|
|
53
48
|
import Workload from '../workload/workload.component';
|
|
49
|
+
import { useSelectedDate } from '../hooks/useSelectedDate';
|
|
54
50
|
import styles from './appointments-form.scss';
|
|
55
51
|
|
|
56
52
|
interface AppointmentsFormProps {
|
|
@@ -84,8 +80,9 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
84
80
|
const locations = useLocations(appointmentLocationTagName);
|
|
85
81
|
const providers = useProviders();
|
|
86
82
|
const session = useSession();
|
|
87
|
-
|
|
88
|
-
const
|
|
83
|
+
|
|
84
|
+
const selectedDate = useSelectedDate();
|
|
85
|
+
const { serviceTypes, isLoading } = useAppointmentServices();
|
|
89
86
|
const { appointmentStatuses, appointmentTypes, allowAllDayAppointments } = useConfig<ConfigObject>();
|
|
90
87
|
|
|
91
88
|
const [isRecurringAppointment, setIsRecurringAppointment] = useState(false);
|
|
@@ -243,7 +240,7 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
243
240
|
appointmentNote: appointment?.comments || '',
|
|
244
241
|
appointmentStatus: appointment?.status || '',
|
|
245
242
|
appointmentType: appointment?.appointmentKind || (appointmentTypes?.length === 1 ? appointmentTypes[0] : ''),
|
|
246
|
-
selectedService: appointment?.service?.name || (
|
|
243
|
+
selectedService: appointment?.service?.name || (serviceTypes?.length === 1 ? serviceTypes[0].name : ''),
|
|
247
244
|
recurringPatternType: defaultRecurringPatternType,
|
|
248
245
|
recurringPatternPeriod: defaultRecurringPatternPeriod,
|
|
249
246
|
recurringPatternDaysOfWeek: defaultRecurringPatternDaysOfWeek,
|
|
@@ -423,7 +420,7 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
423
420
|
isAllDayAppointment,
|
|
424
421
|
} = data;
|
|
425
422
|
|
|
426
|
-
const serviceUuid =
|
|
423
|
+
const serviceUuid = serviceTypes?.find((service) => service.name === selectedService)?.uuid;
|
|
427
424
|
const hoursAndMinutes = startTime.split(':').map((item) => parseInt(item, 10));
|
|
428
425
|
const hours = (hoursAndMinutes[0] % 12) + (timeFormat === 'PM' ? 12 : 0);
|
|
429
426
|
const minutes = hoursAndMinutes[1];
|
|
@@ -535,13 +532,13 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
535
532
|
if (!isEditing) {
|
|
536
533
|
setValue(
|
|
537
534
|
'duration',
|
|
538
|
-
|
|
535
|
+
serviceTypes?.find((service) => service.name === event.target.value)?.durationMins,
|
|
539
536
|
);
|
|
540
537
|
} else {
|
|
541
|
-
const previousServiceDuration =
|
|
538
|
+
const previousServiceDuration = serviceTypes?.find(
|
|
542
539
|
(service) => service.name === getValues('selectedService'),
|
|
543
540
|
)?.durationMins;
|
|
544
|
-
const selectedServiceDuration =
|
|
541
|
+
const selectedServiceDuration = serviceTypes?.find(
|
|
545
542
|
(service) => service.name === event.target.value,
|
|
546
543
|
)?.durationMins;
|
|
547
544
|
if (selectedServiceDuration && previousServiceDuration === getValues('duration')) {
|
|
@@ -553,8 +550,8 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
553
550
|
ref={ref}
|
|
554
551
|
value={value}>
|
|
555
552
|
<SelectItem text={t('chooseService', 'Select service')} value="" />
|
|
556
|
-
{
|
|
557
|
-
|
|
553
|
+
{serviceTypes?.length > 0 &&
|
|
554
|
+
serviceTypes.map((service) => (
|
|
558
555
|
<SelectItem key={service.uuid} text={service.name} value={service.name}>
|
|
559
556
|
{service.name}
|
|
560
557
|
</SelectItem>
|
|
@@ -605,7 +602,7 @@ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps
|
|
|
605
602
|
</div>
|
|
606
603
|
</FormGroup>
|
|
607
604
|
|
|
608
|
-
<FormGroup className={styles.formGroup} legendText={t('dateTime', '
|
|
605
|
+
<FormGroup className={styles.formGroup} legendText={t('dateTime', 'Appointment time')}>
|
|
609
606
|
<div className={styles.dateTimeFields}>
|
|
610
607
|
{isRecurringAppointment && (
|
|
611
608
|
<div className={styles.inputContainer}>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useCallback, useMemo } from 'react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
4
5
|
import { MultiSelect } from '@carbon/react';
|
|
5
6
|
import { PageHeader, PageHeaderContent, AppointmentsPictogram, OpenmrsDatePicker } from '@openmrs/esm-framework';
|
|
6
|
-
import { omrsDateFormat } from '../constants';
|
|
7
7
|
import { useAppointmentServices } from '../hooks/useAppointmentService';
|
|
8
|
-
import {
|
|
8
|
+
import { useSelectedDate } from '../hooks/useSelectedDate';
|
|
9
|
+
import { useAppointmentsStore } from '../store';
|
|
9
10
|
import styles from './appointments-header.scss';
|
|
10
11
|
|
|
11
12
|
interface AppointmentHeaderProps {
|
|
@@ -15,13 +16,21 @@ interface AppointmentHeaderProps {
|
|
|
15
16
|
|
|
16
17
|
const AppointmentsHeader: React.FC<AppointmentHeaderProps> = ({ title, showServiceTypeFilter }) => {
|
|
17
18
|
const { t } = useTranslation();
|
|
18
|
-
const
|
|
19
|
+
const navigate = useNavigate();
|
|
20
|
+
const location = useLocation();
|
|
21
|
+
const { appointmentServiceTypes, setAppointmentServiceTypes } = useAppointmentsStore();
|
|
19
22
|
const { serviceTypes } = useAppointmentServices();
|
|
23
|
+
const selectedDate = useSelectedDate();
|
|
20
24
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
const selectedDateValue = useMemo(() => dayjs(selectedDate).toDate(), [selectedDate]);
|
|
26
|
+
|
|
27
|
+
const handleChangeServiceTypeFilter = useCallback(
|
|
28
|
+
({ selectedItems }) => {
|
|
29
|
+
const selectedUuids = selectedItems.map((item) => item.id);
|
|
30
|
+
setAppointmentServiceTypes(selectedUuids);
|
|
31
|
+
},
|
|
32
|
+
[setAppointmentServiceTypes],
|
|
33
|
+
);
|
|
25
34
|
|
|
26
35
|
const serviceTypeOptions = useMemo(
|
|
27
36
|
() => serviceTypes?.map((item) => ({ id: item.uuid, label: item.name })) ?? [],
|
|
@@ -36,8 +45,17 @@ const AppointmentsHeader: React.FC<AppointmentHeaderProps> = ({ title, showServi
|
|
|
36
45
|
data-testid="appointment-date-picker"
|
|
37
46
|
id="appointment-date-picker"
|
|
38
47
|
aria-label={t('appointmentDate', 'Appointment date')}
|
|
39
|
-
onChange={(date) =>
|
|
40
|
-
|
|
48
|
+
onChange={(date) => {
|
|
49
|
+
if (!date) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const target = `/${dayjs(date).format('YYYY-MM-DD')}`;
|
|
54
|
+
if (!location.pathname.endsWith(target)) {
|
|
55
|
+
navigate(target);
|
|
56
|
+
}
|
|
57
|
+
}}
|
|
58
|
+
value={selectedDateValue}
|
|
41
59
|
/>
|
|
42
60
|
{showServiceTypeFilter && (
|
|
43
61
|
<MultiSelect
|
package/src/helpers/functions.ts
CHANGED
|
@@ -1,58 +1,9 @@
|
|
|
1
1
|
import dayjs, { type Dayjs } from 'dayjs';
|
|
2
2
|
import { type TFunction } from 'i18next';
|
|
3
3
|
import { launchWorkspace2, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
4
|
-
import {
|
|
4
|
+
import { AppointmentStatus } from '../types';
|
|
5
5
|
import { appointmentsFormWorkspace } from '../constants';
|
|
6
6
|
|
|
7
|
-
interface FlattenedAppointmentSummary {
|
|
8
|
-
serviceName: string;
|
|
9
|
-
countMap: AppointmentCountMap[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface ServiceLoadSummary {
|
|
13
|
-
serviceName: string;
|
|
14
|
-
count: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const getHighestAppointmentServiceLoad = (
|
|
18
|
-
appointmentSummary: FlattenedAppointmentSummary[] = [],
|
|
19
|
-
): ServiceLoadSummary | undefined => {
|
|
20
|
-
const groupedAppointments = appointmentSummary.map(({ countMap, serviceName }) => ({
|
|
21
|
-
serviceName: serviceName,
|
|
22
|
-
count: countMap.reduce((accumulator, currentValue) => accumulator + currentValue.allAppointmentsCount, 0),
|
|
23
|
-
}));
|
|
24
|
-
if (groupedAppointments.length === 0) {
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
return groupedAppointments.find((summary) => summary.count === Math.max(...groupedAppointments.map((x) => x.count)));
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const flattenAppointmentSummary = (
|
|
31
|
-
appointmentToTransform: AppointmentSummary[],
|
|
32
|
-
): FlattenedAppointmentSummary[] =>
|
|
33
|
-
appointmentToTransform.flatMap((el) => ({
|
|
34
|
-
serviceName: el.appointmentService.name,
|
|
35
|
-
countMap: Object.entries(el.appointmentCountMap).flatMap(([, countMap]) => countMap),
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
export const getServiceCountByAppointmentType = (
|
|
39
|
-
appointmentSummary: AppointmentSummary[],
|
|
40
|
-
appointmentType: 'allAppointmentsCount' | 'missedAppointmentsCount',
|
|
41
|
-
): number => {
|
|
42
|
-
return appointmentSummary
|
|
43
|
-
.map((el) =>
|
|
44
|
-
Object.values(el.appointmentCountMap).map((countMap) => {
|
|
45
|
-
const value = countMap[appointmentType];
|
|
46
|
-
if (typeof value === 'number') {
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
return 0;
|
|
50
|
-
}),
|
|
51
|
-
)
|
|
52
|
-
.flat(1)
|
|
53
|
-
.reduce((count, val) => count + val, 0);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
7
|
export const formatAMPM = (date: Date): string => {
|
|
57
8
|
const hours24 = date.getHours();
|
|
58
9
|
const minutes = date.getMinutes();
|
|
@@ -2,17 +2,30 @@ import React from 'react';
|
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { toOmrsIsoString } from '@openmrs/esm-framework';
|
|
5
|
-
import
|
|
5
|
+
import AppointmentsTable from '../appointments/common-components/appointments-table.component';
|
|
6
|
+
import { useAppointmentList } from '../hooks/useAppointmentList';
|
|
6
7
|
import styles from './home-appointments.scss';
|
|
8
|
+
import { AppointmentStatus } from '../types';
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* A extension that shows today's appointments, originally meant for the home app.
|
|
12
|
+
*/
|
|
8
13
|
const HomeAppointments = () => {
|
|
9
14
|
const { t } = useTranslation();
|
|
10
15
|
|
|
16
|
+
const today = toOmrsIsoString(dayjs().startOf('day').toDate());
|
|
17
|
+
const { appointmentList, isLoading } = useAppointmentList(today);
|
|
18
|
+
|
|
19
|
+
const excludeCancelledAppointments = appointmentList.filter(
|
|
20
|
+
(appointment) => appointment.status !== AppointmentStatus.CANCELLED,
|
|
21
|
+
);
|
|
22
|
+
|
|
11
23
|
return (
|
|
12
24
|
<div className={styles.container}>
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
<AppointmentsTable
|
|
26
|
+
appointments={excludeCancelledAppointments}
|
|
27
|
+
isLoading={isLoading}
|
|
28
|
+
tableHeading={t('todaysAppointment', "Today's Appointments")}
|
|
16
29
|
/>
|
|
17
30
|
</div>
|
|
18
31
|
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import useSWR from 'swr';
|
|
2
|
+
import { type Visit, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Custom hook to fetch active visits from the OpenMRS REST API.
|
|
6
|
+
* @returns An object containing the visits, isLoading flag, and error message.
|
|
7
|
+
*/
|
|
8
|
+
export const useActiveVisits = () => {
|
|
9
|
+
const visitsUrl = `${restBaseUrl}/visit?includeInactive=false&v=custom:(uuid,patient:(uuid),startDatetime,stopDatetime)`;
|
|
10
|
+
const { data, error, isLoading, mutate } = useSWR<{ data: { results: Visit[] } }>(visitsUrl, openmrsFetch);
|
|
11
|
+
const visits = data?.data?.results ?? [];
|
|
12
|
+
|
|
13
|
+
return { isLoading, visits, error, mutateVisits: mutate };
|
|
14
|
+
};
|
|
@@ -2,40 +2,23 @@ import dayjs from 'dayjs';
|
|
|
2
2
|
import useSWR from 'swr';
|
|
3
3
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
4
4
|
import { type AppointmentsFetchResponse } from '../types';
|
|
5
|
-
import {
|
|
5
|
+
import { useSelectedDate } from './useSelectedDate';
|
|
6
6
|
|
|
7
|
-
export const useAppointmentList = (
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const endDate = dayjs(startDate).endOf('day').format('YYYY-MM-DDTHH:mm:ss.SSSZZ'); // TODO: fix? is this correct?
|
|
11
|
-
const searchUrl = `${restBaseUrl}/appointments/search`;
|
|
12
|
-
const abortController = new AbortController();
|
|
7
|
+
export const useAppointmentList = (date: string) => {
|
|
8
|
+
const startOfDay = dayjs(date).startOf('day').toISOString();
|
|
9
|
+
const searchUrl = `${restBaseUrl}/appointments?forDate=${startOfDay}`;
|
|
13
10
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
method: 'POST',
|
|
17
|
-
signal: abortController.signal,
|
|
18
|
-
headers: {
|
|
19
|
-
'Content-Type': 'application/json',
|
|
20
|
-
},
|
|
21
|
-
body: {
|
|
22
|
-
startDate: startDate,
|
|
23
|
-
endDate: endDate,
|
|
24
|
-
status: status,
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const { data, error, isLoading, mutate } = useSWR<AppointmentsFetchResponse, Error>(
|
|
29
|
-
[searchUrl, startDate, endDate, appointmentStatus],
|
|
30
|
-
fetcher,
|
|
31
|
-
{ errorRetryCount: 2 },
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
return { appointmentList: data?.data ?? [], isLoading, error, mutate };
|
|
11
|
+
const { data, ...rest } = useSWR<AppointmentsFetchResponse, Error>(searchUrl, openmrsFetch);
|
|
12
|
+
return { appointmentList: data?.data ?? [], ...rest };
|
|
35
13
|
};
|
|
36
14
|
|
|
15
|
+
/**
|
|
16
|
+
* This is a non-standard API that does not come with the appointments module by default
|
|
17
|
+
* @param startDate
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
37
20
|
export const useEarlyAppointmentList = (startDate?: string) => {
|
|
38
|
-
const
|
|
21
|
+
const selectedDate = useSelectedDate();
|
|
39
22
|
const forDate = startDate ? startDate : selectedDate;
|
|
40
23
|
const url = `${restBaseUrl}/appointment/earlyAppointment?forDate=${forDate}`;
|
|
41
24
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
1
2
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
3
|
import useSWR from 'swr';
|
|
3
4
|
import { type AppointmentService } from '../types';
|
|
@@ -7,5 +8,9 @@ export function useAppointmentServices() {
|
|
|
7
8
|
`${restBaseUrl}/appointmentService/all/default`,
|
|
8
9
|
openmrsFetch,
|
|
9
10
|
);
|
|
10
|
-
|
|
11
|
+
const sortedServices = useMemo(
|
|
12
|
+
() => (data?.data ? [...data.data].sort((a, b) => a.name.localeCompare(b.name)) : []),
|
|
13
|
+
[data?.data],
|
|
14
|
+
);
|
|
15
|
+
return { serviceTypes: sortedServices, isLoading, error };
|
|
11
16
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useAppContext } from '@openmrs/esm-framework';
|
|
2
|
+
import { type AppointmentsAppContext, type Appointment } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This hook provides a way to reuse fetched appointments data throughout the app.
|
|
6
|
+
* It assumes that:
|
|
7
|
+
* - the ScheduledAppointments component, which calls useDefinedAppContext, is mounted.
|
|
8
|
+
* - It is not used in an extension mounted outside the Appointments app, since it relies
|
|
9
|
+
* on data in useAppointmentsStore().
|
|
10
|
+
*/
|
|
11
|
+
export function useAppointmentsAppContext() {
|
|
12
|
+
return (
|
|
13
|
+
useAppContext<AppointmentsAppContext>('appointments') ?? {
|
|
14
|
+
appointmentForSelectedDateFilteredByServiceTypes: [] as Appointment[],
|
|
15
|
+
error: null,
|
|
16
|
+
isLoading: true,
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -25,10 +25,11 @@ export const useAppointmentsCalendar = (forDate: string, period: string) => {
|
|
|
25
25
|
openmrsFetch,
|
|
26
26
|
{ errorRetryCount: 2 },
|
|
27
27
|
);
|
|
28
|
+
// Transform API response into daily appointment counts grouped by service
|
|
28
29
|
const results: Array<DailyAppointmentsCountByService> = data?.data.reduce((acc, service) => {
|
|
29
30
|
const serviceName = service.appointmentService.name;
|
|
30
31
|
const serviceUuid = service.appointmentService.uuid;
|
|
31
|
-
Object.entries(service.appointmentCountMap).
|
|
32
|
+
Object.entries(service.appointmentCountMap).forEach(([key, value]) => {
|
|
32
33
|
const existingEntry = acc.find((entry) => entry.appointmentDate === key);
|
|
33
34
|
if (existingEntry) {
|
|
34
35
|
existingEntry.services.push({ serviceName, serviceUuid, count: value.allAppointmentsCount });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useSWRConfig } from 'swr';
|
|
3
|
+
import { restBaseUrl } from '@openmrs/esm-framework';
|
|
4
|
+
|
|
5
|
+
const appointmentUrlMatcher = `${restBaseUrl}/appointment`;
|
|
6
|
+
|
|
7
|
+
export function useMutateAppointments() {
|
|
8
|
+
const { mutate } = useSWRConfig();
|
|
9
|
+
// this mutate is intentionally broad because there may be many different keys that need to be invalidated when appointments are updated
|
|
10
|
+
const mutateAppointments = useCallback(
|
|
11
|
+
() =>
|
|
12
|
+
mutate((key) => {
|
|
13
|
+
return (
|
|
14
|
+
(typeof key === 'string' && key.startsWith(appointmentUrlMatcher)) ||
|
|
15
|
+
(Array.isArray(key) && key[0].startsWith(appointmentUrlMatcher))
|
|
16
|
+
);
|
|
17
|
+
}),
|
|
18
|
+
[mutate],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
mutateAppointments,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -2,12 +2,13 @@ import dayjs from 'dayjs';
|
|
|
2
2
|
import useSWR from 'swr';
|
|
3
3
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
4
4
|
import { type AppointmentsFetchResponse } from '../types';
|
|
5
|
-
import {
|
|
5
|
+
import { useSelectedDate } from './useSelectedDate';
|
|
6
6
|
|
|
7
7
|
export function usePatientAppointmentHistory(patientUuid: string) {
|
|
8
8
|
const abortController = new AbortController();
|
|
9
9
|
const appointmentsSearchUrl = `${restBaseUrl}/appointments/search`;
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
const selectedDate = useSelectedDate();
|
|
11
12
|
|
|
12
13
|
const fetcher = () =>
|
|
13
14
|
openmrsFetch(appointmentsSearchUrl, {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A custom hook to get the selected date from the URL parameter. Date should be in YYYY-MM-DD format,
|
|
7
|
+
* although looser strings accepted by dayjs is also fine. If no date is specified, it defaults to the current date.
|
|
8
|
+
*/
|
|
9
|
+
export function useSelectedDate() {
|
|
10
|
+
const params = useParams();
|
|
11
|
+
return useMemo(() => {
|
|
12
|
+
if (params.date) {
|
|
13
|
+
const parsedDate = dayjs(params.date, 'YYYY-MM-DD').startOf('day');
|
|
14
|
+
if (!parsedDate.isValid()) {
|
|
15
|
+
console.warn(
|
|
16
|
+
`Invalid date format in URL parameter: ${params.date}. Format should be YYYY-MM-DD. Falling back to current date.`,
|
|
17
|
+
);
|
|
18
|
+
} else {
|
|
19
|
+
return parsedDate.format('YYYY-MM-DD');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return dayjs().startOf('day').format('YYYY-MM-DD');
|
|
23
|
+
}, [params]);
|
|
24
|
+
}
|
|
@@ -3,6 +3,7 @@ import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
|
3
3
|
import { type Identifier } from '../types';
|
|
4
4
|
import { configSchema } from '../config-schema';
|
|
5
5
|
import { useAppointmentsStore } from '../store';
|
|
6
|
+
import { useSelectedDate } from './useSelectedDate';
|
|
6
7
|
|
|
7
8
|
interface UnscheduledAppointment {
|
|
8
9
|
age: number;
|
|
@@ -19,8 +20,11 @@ interface UnscheduledAppointment {
|
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* This is a non-standard API that does not come with the appointments module by default
|
|
25
|
+
*/
|
|
22
26
|
export function useUnscheduledAppointments() {
|
|
23
|
-
const
|
|
27
|
+
const selectedDate = useSelectedDate();
|
|
24
28
|
// TODO/NOTE: this endpoint is not implemented in main Bahmni Appointments backend
|
|
25
29
|
const url = `${restBaseUrl}/appointment/unScheduledAppointment?forDate=${selectedDate}`;
|
|
26
30
|
const { data, error, isLoading } = useSWR<{ data: Array<UnscheduledAppointment> }>(url, openmrsFetch, {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createDashboard,
|
|
3
3
|
defineConfigSchema,
|
|
4
|
-
defineExtensionConfigSchema,
|
|
5
4
|
getAsyncLifecycle,
|
|
6
5
|
getSyncLifecycle,
|
|
7
6
|
registerBreadcrumbs,
|
|
@@ -9,14 +8,6 @@ import {
|
|
|
9
8
|
import { configSchema } from './config-schema';
|
|
10
9
|
import { createDashboardLink } from './createDashboardLink';
|
|
11
10
|
import { dashboardMeta, appointmentCalendarDashboardMeta, patientChartDashboardMeta } from './dashboard.meta';
|
|
12
|
-
import {
|
|
13
|
-
cancelledAppointmentsPanelConfigSchema,
|
|
14
|
-
checkedInAppointmentsPanelConfigSchema,
|
|
15
|
-
completedAppointmentsPanelConfigSchema,
|
|
16
|
-
earlyAppointmentsPanelConfigSchema,
|
|
17
|
-
expectedAppointmentsPanelConfigSchema,
|
|
18
|
-
missedAppointmentsPanelConfigSchema,
|
|
19
|
-
} from './scheduled-appointments-config-schema';
|
|
20
11
|
|
|
21
12
|
const moduleName = '@openmrs/esm-appointments-app';
|
|
22
13
|
|
|
@@ -32,13 +23,6 @@ export function startupApp() {
|
|
|
32
23
|
|
|
33
24
|
defineConfigSchema(moduleName, configSchema);
|
|
34
25
|
|
|
35
|
-
defineExtensionConfigSchema('expected-appointments-panel', expectedAppointmentsPanelConfigSchema);
|
|
36
|
-
defineExtensionConfigSchema('checked-in-appointments-panel', checkedInAppointmentsPanelConfigSchema);
|
|
37
|
-
defineExtensionConfigSchema('completed-appointments-panel', completedAppointmentsPanelConfigSchema);
|
|
38
|
-
defineExtensionConfigSchema('missed-appointments-panel', missedAppointmentsPanelConfigSchema);
|
|
39
|
-
defineExtensionConfigSchema('cancelled-appointments-panel', cancelledAppointmentsPanelConfigSchema);
|
|
40
|
-
defineExtensionConfigSchema('early-appointments-panel', earlyAppointmentsPanelConfigSchema);
|
|
41
|
-
|
|
42
26
|
registerBreadcrumbs([
|
|
43
27
|
{
|
|
44
28
|
title: 'Appointments',
|
|
@@ -68,11 +52,6 @@ export const appointmentsDashboard = getAsyncLifecycle(() => import('./appointme
|
|
|
68
52
|
|
|
69
53
|
export const homeAppointments = getAsyncLifecycle(() => import('./home/home-appointments.component'), options);
|
|
70
54
|
|
|
71
|
-
export const appointmentsList = getAsyncLifecycle(
|
|
72
|
-
() => import('./appointments/scheduled/appointments-list.component'),
|
|
73
|
-
options,
|
|
74
|
-
);
|
|
75
|
-
|
|
76
55
|
export const earlyAppointments = getAsyncLifecycle(
|
|
77
56
|
() => import('./appointments/scheduled/early-appointments.component'),
|
|
78
57
|
options,
|