(feat) Retrieve scheduled appointments in clinical forms workspace#471
(feat) Retrieve scheduled appointments in clinical forms workspace#471jnsereko wants to merge 7 commits intoopenmrs:mainfrom
Conversation
|
@jnsereko please update the description and add some screenshots or video to provide more context for the reviewers |
|
The CI seems run endlessly so i've cancelled it |
| appointmentKind: string; | ||
| appointmentNumber: string; | ||
| comments: string; | ||
| endDateTime: Date | number; |
There was a problem hiding this comment.
It looks like this property is a Date on the backend. Why are we typing it as Date | number?
There was a problem hiding this comment.
Actually, it returns the number, neither the ISO string, nor a Date object
src/api/index.ts
Outdated
| const filteredAppointments = appointments.filter((appointment) => { | ||
| return !appointment.fulfillingEncounters.includes(encounterUuid); | ||
| }); | ||
| return filteredAppointments.map((appointment) => { | ||
| return updateAppointment(url, appointment, encounterUuid, abortController); | ||
| }); |
There was a problem hiding this comment.
nit: This code would be more concise and easier to read without the explicit return statements
src/api/index.ts
Outdated
| appointmentKind: appointment.appointmentKind, | ||
| status: appointment.status, | ||
| startDateTime: appointment.startDateTime, | ||
| endDateTime: appointment.endDateTime.toString(), |
There was a problem hiding this comment.
Why do we need toString() here? If it's a timestamp, isn't it preferable to convert it to a date string using something like toIsoString or a dayjs utility function instead?
src/api/index.ts
Outdated
| uuid: appointment.uuid | ||
| }; | ||
|
|
||
| return openmrsFetch(`${url}`, { |
There was a problem hiding this comment.
| return openmrsFetch(`${url}`, { | |
| return openmrsFetch(url, { |
src/api/index.ts
Outdated
| export const getPatientAppointment = (appointmentUuid: string) => { | ||
| return openmrsFetch( | ||
| `${restBaseUrl}/appointments/${appointmentUuid}`, | ||
| ).then(({ data }) => { | ||
| if (data) { | ||
| return data; | ||
| } | ||
| return null; | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Would it be useful to have some kind of error handling here? Something along the lines of:
| export const getPatientAppointment = (appointmentUuid: string) => { | |
| return openmrsFetch( | |
| `${restBaseUrl}/appointments/${appointmentUuid}`, | |
| ).then(({ data }) => { | |
| if (data) { | |
| return data; | |
| } | |
| return null; | |
| }); | |
| }; | |
| export const getPatientAppointment = async (appointmentUuid: string) => { | |
| try { | |
| const response = await openmrsFetch(`${restBaseUrl}/appointments/${appointmentUuid}`); | |
| return response.data || null; | |
| } catch (error) { | |
| console.error('Error fetching appointment:', error); | |
| throw error; // Re-throw to allow caller to handle the error | |
| } | |
| }; |
| import { openmrsFetch, restBaseUrl, useOpenmrsSWR } from '@openmrs/esm-framework'; | ||
| import dayjs from 'dayjs'; | ||
| import useSWR, { mutate, SWRResponse } from 'swr'; | ||
| import { type AppointmentsResponse } from '../types'; | ||
| import { useCallback, useMemo, useState } from 'react'; |
There was a problem hiding this comment.
It looks like we're not using openmrsFetch and the SWR imports here. Can we remove them?
There was a problem hiding this comment.
Yes @denniskigen, working on it!
Thanks for the catch!
| } | ||
| // handle appointments | ||
| try { | ||
| const {appointments: myAppointments} = context |
There was a problem hiding this comment.
Looks like we're not using this code
| const appointmentsResponse = await Promise.all(addFulfillingEncounters(abortController, appointments, savedEncounter.uuid)); | ||
| if (appointmentsResponse?.length) { | ||
| showSnackbar({ | ||
| title: translateFn('appointmentsSaved', 'Appointment(s) saved successfully'), | ||
| kind: 'success', | ||
| isLowContrast: true, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Should we modify this so that we only proceed if there are appointments?
if (appointments && appointments.length > 0) {
// ...
}| const appointmentsResponse = await Promise.all(addFulfillingEncounters(abortController, appointments, savedEncounter.uuid)); | ||
| if (appointmentsResponse?.length) { | ||
| showSnackbar({ | ||
| title: translateFn('appointmentsSaved', 'Appointment(s) saved successfully'), |
There was a problem hiding this comment.
Does translateFrom handle pluralization? If not, I think we could just default it to plural:
| title: translateFn('appointmentsSaved', 'Appointment(s) saved successfully'), | |
| title: translateFn('appointmentsSaved', 'Appointments saved successfully'), |
| } catch (error) { | ||
| const errorMessages = Array.isArray(error) ? error.map((err) => err.message) : [error.message]; | ||
| return Promise.reject({ | ||
| title: translateFn('errorSavingAppointments', 'Error saving appointment(s)'), |
There was a problem hiding this comment.
| title: translateFn('errorSavingAppointments', 'Error saving appointment(s)'), | |
| title: translateFn('errorSavingAppointments', 'Error saving appointments'), |
| } | ||
| }; | ||
|
|
||
| const AppointmentsTable = ({ appointments }) => { |
There was a problem hiding this comment.
@jnsereko , never create a component inside another component.
| const updatedAppointment: AppointmentsPayload = { | ||
| fulfillingEncounters: updatedFulfillingEncounters, | ||
| serviceUuid: appointment.service.uuid, | ||
| locationUuid: appointment.location.uuid, | ||
| patientUuid: appointment.patient.uuid, | ||
| dateAppointmentScheduled: appointment.startDateTime, | ||
| appointmentKind: appointment.appointmentKind, | ||
| status: appointment.status, | ||
| startDateTime: appointment.startDateTime, | ||
| endDateTime: toOmrsIsoString(appointment.endDateTime), | ||
| providers: [{ uuid: appointment.providers[0]?.uuid }], | ||
| comments: appointment.comments, | ||
| uuid: appointment.uuid, |
There was a problem hiding this comment.
Isn't this weird that for updating 1 value of the appointment, we need to pass the whole object?
Can this be worked on?
CC: @denniskigen @jnsereko @ibacher @mogoodrich
There was a problem hiding this comment.
Can this be worked on?
Agree with you @vasharma05. A nice improvement to do in the backend
There was a problem hiding this comment.
@jnsereko , can you create a Backend Ticket for this?
There was a problem hiding this comment.
Agree... we should likely have a REST endpoint just for adding a fulfilling encounter to an appointment.
| }); | ||
| } | ||
| launchWorkspace(); | ||
| if (field.meta?.handleAppointmentCreation) { |
There was a problem hiding this comment.
@pirupius @jnsereko, instead of having the check by the workspace name, since workspace names can be changed, I added a check by adding new prop
handleAppointmentCreationin the fieldmeta
Should it really be optional though @vasharma05
| appointmentKind: string; | ||
| appointmentNumber: string; | ||
| comments: string; | ||
| endDateTime: Date | number; |
There was a problem hiding this comment.
Actually, it returns the number, neither the ISO string, nor a Date object
|
Whilst we’re still editing, maybe “(feat) Retrieve scheduled appointments in clinical forms workspace” reads better? |
There was a problem hiding this comment.
So I added one comment but didn't review fully yet. This functionality looks really good! But, unfortunately, if I'm following this correctly I think there's a fundamental problem with it. It looks like this sets the "fulfillingEncounter" is set to the encounter when the appointment was scheduled? Is this what is happening? The "fulfillingEncounters" are meant to be set to any encounters that fulfill the encounter... ie, in this case, it should be the encounter that gets created on March 8th 2025 if/when the patient returns that day. It's not used to store the encounter when the appointment was created. See: https://bahmni.atlassian.net/browse/BAH-3239
Am I understanding this correctly?
fyi @jnsereko
| const updatedAppointment: AppointmentsPayload = { | ||
| fulfillingEncounters: updatedFulfillingEncounters, | ||
| serviceUuid: appointment.service.uuid, | ||
| locationUuid: appointment.location.uuid, | ||
| patientUuid: appointment.patient.uuid, | ||
| dateAppointmentScheduled: appointment.startDateTime, | ||
| appointmentKind: appointment.appointmentKind, | ||
| status: appointment.status, | ||
| startDateTime: appointment.startDateTime, | ||
| endDateTime: toOmrsIsoString(appointment.endDateTime), | ||
| providers: [{ uuid: appointment.providers[0]?.uuid }], | ||
| comments: appointment.comments, | ||
| uuid: appointment.uuid, |
There was a problem hiding this comment.
Agree... we should likely have a REST endpoint just for adding a fulfilling encounter to an appointment.
Thank you @mogoodrich. You are understanding this correctly. IMO, we might need another field for the scheduling encounter or something like that. I have created a talk post on this for further discussion. |
|
@jnsereko Please create a Jira ticket explaining why we're putting this change in and what the goals are. |
Requirements
Summary
This PR adds ability to
Requirements
cc-ing @vasharma05 @pirupius @ibacher @denniskigen
Screenshots
Screen.Recording.2025-02-27.at.16.10.27.mov
Related Issue
[should be created]
Other