Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) O3-2793 add actions to transition queue entries in queue table #997

Merged
merged 6 commits into from
Feb 27, 2024

Conversation

chibongho
Copy link
Contributor

@chibongho chibongho commented Feb 22, 2024

Requirements

  • This PR has a title that briefly describes the work done including the ticket number. If there is a ticket, make sure your PR title includes a conventional commit label. See existing PR titles for inspiration.
  • My work conforms to the OpenMRS 3.0 Styleguide and design documentation.
  • My work includes tests or is validated by existing tests.

Summary

This PR adds "transition" actions to the queue table. A transition ends a queue entry and immediately starts a new one, albeit with (slightly) different values. This allows for us to keep a history of all queue entries over transitions in the patient's visit. The backend support for this is recently added by this. This PR contains actions to transition the queue entry status, queue entry priority and which queue a patient is on (i.e. a transfer to a different queue).

Screenshots

The modal dialogs for the transitions are very similar to the dialogs already there for queue transfers in the older <ActiveVisitsTable /> (but makes different backend calls).

image
image
image
image

Related Issue

Other

Copy link
Contributor

github-actions bot commented Feb 22, 2024

Size Change: -54.6 kB (-2%)

Total Size: 2.97 MB

Filename Size Change
packages/esm-service-queues-app/dist/621.js 0 B -55 kB (removed) 🏆
ℹ️ View Unchanged
Filename Size Change
packages/esm-active-visits-app/dist/130.js 123 kB 0 B
packages/esm-active-visits-app/dist/255.js 2.21 kB 0 B
packages/esm-active-visits-app/dist/271.js 762 B 0 B
packages/esm-active-visits-app/dist/277.js 13.4 kB 0 B
packages/esm-active-visits-app/dist/316.js 42.9 kB 0 B
packages/esm-active-visits-app/dist/319.js 683 B 0 B
packages/esm-active-visits-app/dist/382.js 1.15 kB 0 B
packages/esm-active-visits-app/dist/448.js 47.1 kB 0 B
packages/esm-active-visits-app/dist/460.js 784 B 0 B
packages/esm-active-visits-app/dist/574.js 588 B 0 B
packages/esm-active-visits-app/dist/588.js 6.66 kB 0 B
packages/esm-active-visits-app/dist/635.js 1.15 kB 0 B
packages/esm-active-visits-app/dist/644.js 762 B 0 B
packages/esm-active-visits-app/dist/729.js 3.1 kB 0 B
packages/esm-active-visits-app/dist/757.js 695 B 0 B
packages/esm-active-visits-app/dist/784.js 2.63 kB 0 B
packages/esm-active-visits-app/dist/788.js 586 B 0 B
packages/esm-active-visits-app/dist/807.js 918 B 0 B
packages/esm-active-visits-app/dist/833.js 732 B 0 B
packages/esm-active-visits-app/dist/879.js 2.94 kB 0 B
packages/esm-active-visits-app/dist/main.js 65 kB 0 B
packages/esm-active-visits-app/dist/openmrs-esm-active-visits-app.js 3.33 kB 0 B
packages/esm-appointments-app/dist/130.js 123 kB 0 B
packages/esm-appointments-app/dist/152.js 257 B 0 B
packages/esm-appointments-app/dist/255.js 2.21 kB 0 B
packages/esm-appointments-app/dist/271.js 2.19 kB 0 B
packages/esm-appointments-app/dist/303.js 258 B 0 B
packages/esm-appointments-app/dist/319.js 2.1 kB 0 B
packages/esm-appointments-app/dist/426.js 271 kB 0 B
packages/esm-appointments-app/dist/460.js 2.29 kB 0 B
packages/esm-appointments-app/dist/500.js 2.5 kB 0 B
packages/esm-appointments-app/dist/574.js 1.85 kB 0 B
packages/esm-appointments-app/dist/588.js 6.65 kB 0 B
packages/esm-appointments-app/dist/591.js 16.9 kB 0 B
packages/esm-appointments-app/dist/644.js 2.19 kB 0 B
packages/esm-appointments-app/dist/693.js 48.7 kB 0 B
packages/esm-appointments-app/dist/729.js 3.1 kB 0 B
packages/esm-appointments-app/dist/757.js 1.85 kB 0 B
packages/esm-appointments-app/dist/784.js 2.63 kB 0 B
packages/esm-appointments-app/dist/788.js 1.85 kB 0 B
packages/esm-appointments-app/dist/807.js 2.53 kB 0 B
packages/esm-appointments-app/dist/833.js 2.16 kB 0 B
packages/esm-appointments-app/dist/main.js 323 kB 0 B
packages/esm-appointments-app/dist/openmrs-esm-appointments-app.js 3.33 kB 0 B
packages/esm-patient-list-management-app/dist/130.js 123 kB 0 B
packages/esm-patient-list-management-app/dist/255.js 2.21 kB 0 B
packages/esm-patient-list-management-app/dist/271.js 1.56 kB 0 B
packages/esm-patient-list-management-app/dist/295.js 99.3 kB 0 B
packages/esm-patient-list-management-app/dist/319.js 1.52 kB 0 B
packages/esm-patient-list-management-app/dist/382.js 1.15 kB 0 B
packages/esm-patient-list-management-app/dist/435.js 22.7 kB 0 B
packages/esm-patient-list-management-app/dist/460.js 1.7 kB 0 B
packages/esm-patient-list-management-app/dist/574.js 1.34 kB 0 B
packages/esm-patient-list-management-app/dist/588.js 6.66 kB 0 B
packages/esm-patient-list-management-app/dist/591.js 16.9 kB 0 B
packages/esm-patient-list-management-app/dist/635.js 1.15 kB 0 B
packages/esm-patient-list-management-app/dist/644.js 1.56 kB 0 B
packages/esm-patient-list-management-app/dist/716.js 4.66 kB 0 B
packages/esm-patient-list-management-app/dist/729.js 3.1 kB 0 B
packages/esm-patient-list-management-app/dist/757.js 1.5 kB 0 B
packages/esm-patient-list-management-app/dist/784.js 2.64 kB 0 B
packages/esm-patient-list-management-app/dist/788.js 1.34 kB 0 B
packages/esm-patient-list-management-app/dist/807.js 1.84 kB 0 B
packages/esm-patient-list-management-app/dist/833.js 1.58 kB 0 B
packages/esm-patient-list-management-app/dist/main.js 126 kB 0 B
packages/esm-patient-list-management-app/dist/openmrs-esm-patient-list-management-app.js 3.3 kB 0 B
packages/esm-patient-registration-app/dist/130.js 123 kB 0 B
packages/esm-patient-registration-app/dist/152.js 262 B 0 B
packages/esm-patient-registration-app/dist/209.js 36.4 kB 0 B
packages/esm-patient-registration-app/dist/255.js 2.21 kB 0 B
packages/esm-patient-registration-app/dist/271.js 2.01 kB 0 B
packages/esm-patient-registration-app/dist/303.js 260 B 0 B
packages/esm-patient-registration-app/dist/319.js 1.99 kB 0 B
packages/esm-patient-registration-app/dist/460.js 2.12 kB 0 B
packages/esm-patient-registration-app/dist/537.js 2.34 kB 0 B
packages/esm-patient-registration-app/dist/574.js 1.7 kB 0 B
packages/esm-patient-registration-app/dist/591.js 16.9 kB 0 B
packages/esm-patient-registration-app/dist/62.js 6.86 kB 0 B
packages/esm-patient-registration-app/dist/644.js 2.01 kB 0 B
packages/esm-patient-registration-app/dist/729.js 3.1 kB 0 B
packages/esm-patient-registration-app/dist/730.js 115 kB 0 B
packages/esm-patient-registration-app/dist/735.js 464 B 0 B
packages/esm-patient-registration-app/dist/757.js 2.07 kB 0 B
packages/esm-patient-registration-app/dist/784.js 2.64 kB 0 B
packages/esm-patient-registration-app/dist/788.js 1.71 kB 0 B
packages/esm-patient-registration-app/dist/807.js 2.43 kB 0 B
packages/esm-patient-registration-app/dist/833.js 1.97 kB 0 B
packages/esm-patient-registration-app/dist/879.js 2.94 kB 0 B
packages/esm-patient-registration-app/dist/884.js 6.1 kB 0 B
packages/esm-patient-registration-app/dist/main.js 153 kB 0 B
packages/esm-patient-registration-app/dist/openmrs-esm-patient-registration-app.js 3.34 kB 0 B
packages/esm-patient-search-app/dist/130.js 123 kB 0 B
packages/esm-patient-search-app/dist/152.js 261 B 0 B
packages/esm-patient-search-app/dist/255.js 2.21 kB 0 B
packages/esm-patient-search-app/dist/271.js 1.12 kB 0 B
packages/esm-patient-search-app/dist/303.js 260 B 0 B
packages/esm-patient-search-app/dist/319.js 1.06 kB 0 B
packages/esm-patient-search-app/dist/382.js 1.15 kB 0 B
packages/esm-patient-search-app/dist/460.js 1.16 kB 0 B
packages/esm-patient-search-app/dist/48.js 26.3 kB 0 B
packages/esm-patient-search-app/dist/574.js 910 B 0 B
packages/esm-patient-search-app/dist/588.js 6.66 kB 0 B
packages/esm-patient-search-app/dist/591.js 16.9 kB 0 B
packages/esm-patient-search-app/dist/635.js 1.15 kB 0 B
packages/esm-patient-search-app/dist/644.js 1.12 kB 0 B
packages/esm-patient-search-app/dist/710.js 22.8 kB 0 B
packages/esm-patient-search-app/dist/729.js 3.1 kB 0 B
packages/esm-patient-search-app/dist/757.js 1.06 kB 0 B
packages/esm-patient-search-app/dist/784.js 2.63 kB 0 B
packages/esm-patient-search-app/dist/788.js 905 B 0 B
packages/esm-patient-search-app/dist/807.js 1.3 kB 0 B
packages/esm-patient-search-app/dist/833.js 1.08 kB 0 B
packages/esm-patient-search-app/dist/main.js 52.3 kB 0 B
packages/esm-patient-search-app/dist/openmrs-esm-patient-search-app.js 3.34 kB 0 B
packages/esm-service-queues-app/dist/130.js 123 kB 0 B
packages/esm-service-queues-app/dist/149.js 3.23 kB 0 B
packages/esm-service-queues-app/dist/152.js 262 B 0 B
packages/esm-service-queues-app/dist/255.js 2.22 kB +6 B (0%)
packages/esm-service-queues-app/dist/271.js 3.71 kB 0 B
packages/esm-service-queues-app/dist/303.js 261 B 0 B
packages/esm-service-queues-app/dist/319.js 3.19 kB 0 B
packages/esm-service-queues-app/dist/328.js 3.08 kB 0 B
packages/esm-service-queues-app/dist/389.js 2.43 kB 0 B
packages/esm-service-queues-app/dist/425.js 2.07 kB 0 B
packages/esm-service-queues-app/dist/460.js 3.98 kB 0 B
packages/esm-service-queues-app/dist/574.js 3.29 kB +102 B (+3%)
packages/esm-service-queues-app/dist/581.js 156 kB 0 B
packages/esm-service-queues-app/dist/588.js 6.66 kB 0 B
packages/esm-service-queues-app/dist/591.js 16.9 kB 0 B
packages/esm-service-queues-app/dist/616.js 2.71 kB 0 B
packages/esm-service-queues-app/dist/644.js 3.71 kB 0 B
packages/esm-service-queues-app/dist/694.js 2.64 kB 0 B
packages/esm-service-queues-app/dist/713.js 55.2 kB 0 B
packages/esm-service-queues-app/dist/729.js 3.1 kB 0 B
packages/esm-service-queues-app/dist/757.js 3.19 kB 0 B
packages/esm-service-queues-app/dist/784.js 2.63 kB 0 B
packages/esm-service-queues-app/dist/788.js 3.2 kB +15 B (0%)
packages/esm-service-queues-app/dist/807.js 4.45 kB 0 B
packages/esm-service-queues-app/dist/833.js 3.67 kB 0 B
packages/esm-service-queues-app/dist/981.js 2.93 kB 0 B
packages/esm-service-queues-app/dist/main.js 214 kB +233 B (0%)
packages/esm-service-queues-app/dist/openmrs-esm-service-queues-app.js 3.31 kB 0 B

compressed-size-action

Copy link
Member

@mogoodrich mogoodrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @chibongho !

Copy link
Member

@mseaton mseaton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chibongho - The code for this looks well done. My question is more around the requirements. I generally would not expect to have different menu items to modify each property of a queue entry individually, and I would not want to transition twice if I wanted to transition both the status and the priority of a queue entry.

There are also several additional properties of a Queue Entry that are not handled here, and we need to think about where these would fit in for implementations that might want them, namely locationWaitingFor, providerWaitingFor, and priorityComments.

The way I've been thinking of it is that we need to clearly present 2 basic options - one option is to modify the existing queue entry in place (eg. edit the status or priority, or these other fields, without creating a new queue entry) - and the other is to transition to a new queue entry.

Both of these options could likely use the same form, and just limit allowing one to change the queue itself to the "transition" case.

This likely needs a bit more analysis, but I think having 2 options (eg. Edit and Transition) that both open a form that provides the ability to enter all of the available fields, provides the most flexibility and avoid creating more queue entries than is needed/desired.

},
);

export const transferQueueModal = getAsyncLifecycle(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the naming call this a Modal and the others are Dialog?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer Modal for consistency

import { useTranslation } from 'react-i18next';
import { type QueueTableCellComponentProps, type QueueTableColumn } from '../../types';

export function QueueTableActionsCell({ queueEntry }: QueueTableCellComponentProps) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't there really 2 different cells here? One is a cell containing a "Transfer" button. The other is a cell containing an "Actions" menu. I could see different implementations wanting one and not the other.

@chibongho
Copy link
Contributor Author

@chibongho - The code for this looks well done. My question is more around the requirements. I generally would not expect to have different menu items to modify each property of a queue entry individually, and I would not want to transition twice if I wanted to transition both the status and the priority of a queue entry.

@mogoodrich and I discussed this a little (albeit in a vacuum just between us). The idea is that the chance of us having to change both status and priority at the same time is slim, and that having to transition twice is not that terrible. You brought up a good point with the other possible fields to change (like priorityComments, locationWaitingFor, providerWaitingFor and sortWeight). So maybe we should have one form that's flexible enough to modify any/all fields we want, and also be able to distinguish between a transition and an edit.

There are also several additional properties of a Queue Entry that are not handled here, and we need to think about where these would fit in for implementations that might want them, namely locationWaitingFor, providerWaitingFor, and priorityComments.

What is locationWaitingFor and providerWaitingFor (and why do we need those in addition to which queue they are in?) Is that documented somewhere?

The way I've been thinking of it is that we need to clearly present 2 basic options - one option is to modify the existing queue entry in place (eg. edit the status or priority, or these other fields, without creating a new queue entry) - and the other is to transition to a new queue entry.

Both of these options could likely use the same form, and just limit allowing one to change the queue itself to the "transition" case.

This likely needs a bit more analysis, but I think having 2 options (eg. Edit and Transition) that both open a form that provides the ability to enter all of the available fields, provides the most flexibility and avoid creating more queue entries than is needed/desired.

Yeah, I like that. Will need more clarification from Paul/Ciaran on UI to distinguish between edits and transition.

@ibacher
Copy link
Member

ibacher commented Feb 23, 2024

What is locationWaitingFor and providerWaitingFor (and why do we need those in addition to which queue they are in?) Is that documented somewhere?

If you like gory details, most of the discussion around how queues were designed is on Talk. However, the idea was basically that a queue is waiting for a resource; in the discussions, the "resources" that people might be waiting for were: a service (which is what this app defaults to assuming everything is about), a location (specifically something like "Room 123"), or a specific provider. Technically, the queue module was supposed to be an evolution of the patientqueueing module, which always represents the resource the queue is waiting for as a location.

Copy link
Member

@denniskigen denniskigen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @chibongho. Your code looks good. I've left a few comments.

const apiUrl = '/ws/rest/v1/queue' + (locationUuid ? `?location=${locationUuid}` : '');
const customRepresentation =
'custom:(uuid,display,name,description,allowedPriorities:(uuid,display),allowedStatuses:(uuid,display),location:(uuid,display))';
const apiUrl = `/ws/rest/v1/queue?v=${customRepresentation}` + (locationUuid ? `&location=${locationUuid}` : '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const apiUrl = `/ws/rest/v1/queue?v=${customRepresentation}` + (locationUuid ? `&location=${locationUuid}` : '');
const apiUrl = `${restBaseUrl}/queue?v=${customRepresentation}` + (locationUuid ? `&location=${locationUuid}` : '');

const { mutate } = useSWRConfig();
const mutateQueueEntries = () =>
mutate((key) => {
return typeof key === 'string' && key.startsWith(`/ws/rest/v1/queue-entry`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return typeof key === 'string' && key.startsWith(`/ws/rest/v1/queue-entry`);
return typeof key === 'string' && key.startsWith(`${restBaseUrl}/queue-entry`);

},
);

export const transferQueueModal = getAsyncLifecycle(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer Modal for consistency

Comment on lines 24 to 31
<OverflowMenuItem
itemText={t('transitionStatus', 'Transition status')}
onClick={() => {
const dispose = showModal('transition-queue-entry-status-dialog', {
closeModal: () => dispose(),
queueEntry,
});
}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll likely want to give these OverflowMenuItem components a className which applies a max-width of 'none':

.menuItem {
  max-width: none;
}

Comment on lines 19 to 21
useConfig: jest.fn(() => ({
customPatientChartUrl: 'someUrl',
})),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brandones introduced an alternative approach for overriding config schemas. Worth checking out.

Copy link
Contributor

@brandones brandones Feb 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'd suggest only mocking useConfig if you actually are testing config options. If you just need to get your components to render with the default config, you can just add defineConfigSchema('@openmrs/esm-service-queues-app', configSchema) at the top of the file and the default esm-framework mock will handle it for you. mockedUseConfig.mockReturnValue(getDefaultsFromConfigSchema(configSchema)); is a good alternative if you'll want to test configurability later.

.then(({ status }) => {
if (status === 200) {
showSnackbar({
isLowContrast: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as an FYI, this prop can be omitted if you're on the latest version of the framework. It defaults to true if omitted. Same for the success prop.

invalidText="Required"
value={formState.selectedQueue}
onChange={(event) => setSelectedQueueUuid(event.target.value)}>
{queues?.map((queue) => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worthwhile adding a default state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussing with @mseaton more, we are consolidating transitions (of queues and/or status) into a single form. I think this means we should default to the currently selected queue (in case the user only wants to transition the status).

@@ -0,0 +1,78 @@
import { openmrsFetch, showSnackbar } from '@openmrs/esm-framework';
import '@testing-library/jest-dom';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already included in setup-tests.ts.


renderWithSwr(<TransferQueueDialog queueEntry={queueEntry} closeModal={closeModal} />);
const cancelButton = screen.getByText('Cancel');
cancelButton.click();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await user.click(cancelButton);


return (
<div>
<Form onSubmit={transferQueue}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<Form onSubmit={transferQueue}>
<Form onSubmit={transferQueue}>
<Stack gap={4}>

I recommend using a Stack to space out the form fields in all of these modals.

@chibongho
Copy link
Contributor Author

PR updated. Addressed PR comments. After discussing with @mseaton, we are also consolidating the transitions into one single form. The form validates that either the status or the queue is different from current values for the submit button to be enabled.

Copy link
Member

@mseaton mseaton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @chibongho !

@chibongho chibongho merged commit 687084e into main Feb 27, 2024
6 checks passed
@chibongho chibongho deleted the O3-2793 branch February 27, 2024 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants