Skip to content

Commit 64a6d04

Browse files
authored
(feat) O3-4985: Handle duplicate queue entries in the move and transition modals (#1944)
Augments the queue entry actions modal to handle duplicate queue entries when transitioning a patient to a different queue. Following openmrs/openmrs-module-queue@4dd3887, the queue module now prevents ending existing queue entries when transitioning patients to different queues if they already have an entry in the target queue. This PR also: - Replaces snackbar notifications with inline error handling for better error visibility - Extends the QueueEntryActionModal test suite to cover the new error handling logic
1 parent a71cb27 commit 64a6d04

File tree

2 files changed

+303
-17
lines changed

2 files changed

+303
-17
lines changed

packages/esm-service-queues-app/src/modals/queue-entry-actions-modal.component.tsx

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React, { useMemo, useState } from 'react';
2+
import classNames from 'classnames';
23
import dayjs from 'dayjs';
34
import {
45
Button,
56
Checkbox,
7+
Dropdown,
68
InlineNotification,
79
ModalBody,
810
ModalFooter,
@@ -14,20 +16,17 @@ import {
1416
TextArea,
1517
TimePicker,
1618
TimePickerSelect,
17-
Dropdown,
18-
Tag,
1919
} from '@carbon/react';
2020
import { useTranslation } from 'react-i18next';
2121
import { OpenmrsDatePicker, showSnackbar, type FetchResponse, useConfig } from '@openmrs/esm-framework';
22-
import { time12HourFormatRegexPattern } from '../constants';
2322
import { convertTime12to24, type amPm } from '../helpers/time-helpers';
24-
import { useMutateQueueEntries } from '../hooks/useQueueEntries';
25-
import { useQueues } from '../hooks/useQueues';
23+
import { DUPLICATE_QUEUE_ENTRY_ERROR_CODE, time12HourFormatRegexPattern } from '../constants';
2624
import { type ConfigObject } from '../config-schema';
2725
import { type QueueEntry } from '../types';
28-
import styles from './queue-entry-actions.scss';
29-
import classNames from 'classnames';
26+
import { useMutateQueueEntries } from '../hooks/useQueueEntries';
27+
import { useQueues } from '../hooks/useQueues';
3028
import QueuePriority from '../queue-table/components/queue-priority.component';
29+
import styles from './queue-entry-actions.scss';
3130

3231
interface QueueEntryActionModalProps {
3332
queueEntry: QueueEntry;
@@ -103,15 +102,23 @@ export const QueueEntryActionModal: React.FC<QueueEntryActionModalProps> = ({
103102
});
104103
const { queues } = useQueues();
105104
const [isSubmitting, setIsSubmitting] = useState(false);
105+
const [submissionError, setSubmissionError] = useState<{
106+
type: 'duplicate' | 'error';
107+
message: string;
108+
title?: string;
109+
} | null>(null);
106110

107111
const selectedQueue = queues.find((q) => q.uuid == formState.selectedQueue);
108112

113+
const clearSubmissionError = () => setSubmissionError(null);
114+
109115
const statuses = selectedQueue?.allowedStatuses;
110116
const hasNoStatusesConfigured = selectedQueue && statuses.length == 0;
111117
const priorities = selectedQueue?.allowedPriorities;
112118
const hasNoPrioritiesConfigured = selectedQueue && priorities.length == 0;
113119

114120
const setSelectedQueueUuid = (selectedQueueUuid: string) => {
121+
clearSubmissionError();
115122
const newSelectedQueue = queues.find((q) => q.uuid == selectedQueueUuid);
116123
const { allowedStatuses, allowedPriorities } = newSelectedQueue;
117124
const newQueueHasCurrentPriority = allowedPriorities.find((s) => s.uuid == formState.selectedPriority);
@@ -128,10 +135,12 @@ export const QueueEntryActionModal: React.FC<QueueEntryActionModalProps> = ({
128135
};
129136

130137
const setSelectedPriorityUuid = (selectedPriorityUuid: string) => {
138+
clearSubmissionError();
131139
setFormState({ ...formState, selectedPriority: selectedPriorityUuid });
132140
};
133141

134142
const setSelectedStatusUuid = (selectedStatusUuid: string) => {
143+
clearSubmissionError();
135144
setFormState({ ...formState, selectedStatus: selectedStatusUuid });
136145
};
137146

@@ -155,10 +164,6 @@ export const QueueEntryActionModal: React.FC<QueueEntryActionModalProps> = ({
155164
setFormState({ ...formState, modifyDefaultTransitionDateTime });
156165
};
157166

158-
const findPriorityIndex = (uuid: string) => {
159-
return priorities.findIndex((p) => p.uuid === uuid);
160-
};
161-
162167
const submitForm = (e) => {
163168
e.preventDefault();
164169
setIsSubmitting(true);
@@ -179,11 +184,22 @@ export const QueueEntryActionModal: React.FC<QueueEntryActionModalProps> = ({
179184
}
180185
})
181186
.catch((error) => {
182-
showSnackbar({
183-
title: submitFailureTitle,
184-
kind: 'error',
185-
subtitle: error?.message,
186-
});
187+
const errorMessage = error?.responseBody?.error?.message || error?.message || '';
188+
const isDuplicateQueueEntryError = errorMessage.includes(DUPLICATE_QUEUE_ENTRY_ERROR_CODE);
189+
190+
if (isDuplicateQueueEntryError) {
191+
setSubmissionError({
192+
type: 'duplicate',
193+
message: t('duplicateQueueEntry', 'This patient is already in the selected queue.'),
194+
title: t('patientAlreadyInQueue', 'Patient already in queue'),
195+
});
196+
} else {
197+
setSubmissionError({
198+
type: 'error',
199+
message: error?.message || t('unknownError', 'An unknown error occurred'),
200+
title: submitFailureTitle,
201+
});
202+
}
187203
})
188204
.finally(() => {
189205
setIsSubmitting(false);
@@ -397,6 +413,16 @@ export const QueueEntryActionModal: React.FC<QueueEntryActionModalProps> = ({
397413
</div>
398414
)}
399415
</section>
416+
417+
{submissionError && (
418+
<InlineNotification
419+
kind="error"
420+
lowContrast={false}
421+
title={submissionError.title || t('queueEntryError', 'Error updating queue entry')}
422+
subtitle={submissionError.message}
423+
onClose={() => setSubmissionError(null)}
424+
/>
425+
)}
400426
</Stack>
401427
</div>
402428
</ModalBody>

0 commit comments

Comments
 (0)