From 3e16a00d907a0c39d134706e1710968bf86f05b3 Mon Sep 17 00:00:00 2001 From: chibongho Date: Fri, 6 Dec 2024 22:12:33 -0500 Subject: [PATCH] =?UTF-8?q?(feat)=20-=20O3-4200=20Service=20queues=20-=20u?= =?UTF-8?q?se=20visit=20form=20in=20patient=20chart=20for=E2=80=A6=20(#140?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (feat) - O3-4200 Service queues - use visit form in patient chart for create queue entry workflow * rename slot names --- __mocks__/index.ts | 2 +- ...s.mock.ts => patient-appointments.mock.ts} | 6 +- .../checkin-button.component.tsx | 6 +- ...t-upcoming-appointments-card.component.tsx | 58 ++- packages/esm-appointments-app/src/routes.json | 2 +- .../esm-appointments-app/translations/en.json | 3 + .../active-visits-table.resource.ts | 62 ---- .../change-status-dialog.component.tsx | 2 +- .../change-status-dialog.test.tsx | 2 +- .../add-provider-queue-room.component.tsx | 2 +- .../add-provider-queue-room.test.tsx | 2 +- .../create-queue-entry.scss} | 0 .../create-queue-entry.workspace.tsx | 99 +++++ .../existing-visit-form.component.tsx | 69 ++++ .../existing-visit-form.scss} | 0 .../hooks/useQueueLocations.tsx | 0 .../queue-fields/queue-fields.component.tsx} | 99 +++-- .../queue-fields/queue-fields.resource.ts | 63 ++++ .../queue-fields/queue-fields.scss} | 0 .../queue-fields/queue-fields.test.tsx} | 51 ++- .../visit-form-queue-fields.extension.tsx | 28 ++ packages/esm-service-queues-app/src/index.ts | 14 +- .../patient-queue-header.component.tsx | 2 +- .../empty-data-illustration.component.tsx | 41 --- .../hooks/useActivePatientEnrollment.tsx | 29 -- .../hooks/useDefaultLocation.ts | 14 - .../hooks/useRecommendedVisitTypes.tsx | 35 -- .../hooks/useScheduledVisits.ts | 52 --- .../patient-scheduled-visits.component.tsx | 313 ---------------- .../patient-scheduled-visits.scss | 131 ------- .../patient-scheduled-visits.test.tsx | 39 -- .../patient-search.workspace.tsx | 135 ------- .../existing-visit-form.component.tsx | 124 ------- .../visit-form/queue.resource.ts | 64 ---- .../visit-form/visit-form.component.tsx | 342 ------------------ .../visit-type-selector.component.tsx | 153 -------- .../visit-form/visit-type-selector.scss | 100 ----- .../visit-form/visit-type-selector.test.tsx | 84 ----- .../src/queue-rooms/queue-room-form.test.tsx | 4 +- .../queue-rooms/queue-room-form.workspace.tsx | 2 +- .../queue-service-form.test.tsx | 2 +- .../queue-service-form.workspace.tsx | 2 +- .../default-queue-table.component.tsx | 2 +- .../queue-table/default-queue-table.test.tsx | 6 +- .../esm-service-queues-app/src/routes.json | 8 +- .../esm-service-queues-app/src/types/index.ts | 8 - ...ueue-tables-for-all-statuses.component.tsx | 2 +- .../translations/en.json | 31 +- 48 files changed, 453 insertions(+), 1842 deletions(-) rename __mocks__/{patient-visits.mock.ts => patient-appointments.mock.ts} (97%) rename packages/esm-service-queues-app/src/{patient-search/patient-search.scss => create-queue-entry/create-queue-entry.scss} (100%) create mode 100644 packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.workspace.tsx create mode 100644 packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.component.tsx rename packages/esm-service-queues-app/src/{patient-search/visit-form/visit-form.scss => create-queue-entry/existing-visit-form/existing-visit-form.scss} (100%) rename packages/esm-service-queues-app/src/{patient-search => create-queue-entry}/hooks/useQueueLocations.tsx (100%) rename packages/esm-service-queues-app/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx => create-queue-entry/queue-fields/queue-fields.component.tsx} (71%) create mode 100644 packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.resource.ts rename packages/esm-service-queues-app/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.scss => create-queue-entry/queue-fields/queue-fields.scss} (100%) rename packages/esm-service-queues-app/src/{patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx => create-queue-entry/queue-fields/queue-fields.test.tsx} (59%) create mode 100644 packages/esm-service-queues-app/src/create-queue-entry/queue-fields/visit-form-queue-fields.extension.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/empty-data-illustration.component.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/hooks/useActivePatientEnrollment.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/hooks/useDefaultLocation.ts delete mode 100644 packages/esm-service-queues-app/src/patient-search/hooks/useRecommendedVisitTypes.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/hooks/useScheduledVisits.ts delete mode 100644 packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.component.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.scss delete mode 100644 packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.test.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/patient-search.workspace.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/existing-visit-form.component.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.component.tsx delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.scss delete mode 100644 packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.test.tsx diff --git a/__mocks__/index.ts b/__mocks__/index.ts index 0fc40834c..15692d4f8 100644 --- a/__mocks__/index.ts +++ b/__mocks__/index.ts @@ -8,7 +8,7 @@ export * from './identifiers.mock'; export * from './locations.mock'; export * from './metrics.mock'; export * from './patient.mock'; -export * from './patient-visits.mock'; +export * from './patient-appointments.mock'; export * from './patient-registration.mock'; export * from './queue-entry.mock'; export * from './queue-rooms.mock'; diff --git a/__mocks__/patient-visits.mock.ts b/__mocks__/patient-appointments.mock.ts similarity index 97% rename from __mocks__/patient-visits.mock.ts rename to __mocks__/patient-appointments.mock.ts index e5b106e5c..07055ca5d 100644 --- a/__mocks__/patient-visits.mock.ts +++ b/__mocks__/patient-appointments.mock.ts @@ -1,5 +1,5 @@ -export const mockPatientsVisits = { - recentVisits: [ +export const mockPatientAppointments = { + recentAppointments: [ { uuid: '6baa7963-68ea-497e-b258-6fb82382bd07', appointmentNumber: '0000', @@ -59,7 +59,7 @@ export const mockPatientsVisits = { recurring: false, }, ], - futureVisits: [ + futureAppointments: [ { uuid: '6baa7963-68ea-497e-b258-6fb82382bd07', appointmentNumber: '0000', diff --git a/packages/esm-appointments-app/src/appointments/common-components/checkin-button.component.tsx b/packages/esm-appointments-app/src/appointments/common-components/checkin-button.component.tsx index 85ac5c605..173428f77 100644 --- a/packages/esm-appointments-app/src/appointments/common-components/checkin-button.component.tsx +++ b/packages/esm-appointments-app/src/appointments/common-components/checkin-button.component.tsx @@ -31,7 +31,11 @@ const CheckInButton: React.FC = ({ appointment, patientUuid to: checkInButton.customUrl, templateParams: { patientUuid: appointment.patient.uuid, appointmentUuid: appointment.uuid }, }) - : launchWorkspace('start-visit-workspace-form', { patientUuid: patientUuid, showPatientHeader: true }) + : launchWorkspace('start-visit-workspace-form', { + patientUuid: patientUuid, + showPatientHeader: true, + openedFrom: 'appointments-check-in', + }) }> {t('checkIn', 'Check in')} diff --git a/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx b/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx index 7c070be92..cee3684ad 100644 --- a/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx +++ b/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx @@ -10,31 +10,72 @@ import { StructuredListRow, StructuredListWrapper, } from '@carbon/react'; -import { formatDate, parseDate } from '@openmrs/esm-framework'; -import { usePatientAppointments } from './patient-appointments.resource'; +import { formatDate, parseDate, showSnackbar, type Visit } from '@openmrs/esm-framework'; +import { changeAppointmentStatus, usePatientAppointments } from './patient-appointments.resource'; import { ErrorState } from '@openmrs/esm-patient-common-lib'; import styles from './patient-upcoming-appointments-card.scss'; import dayjs from 'dayjs'; import { type Appointment } from '../types'; +import { useMutateAppointments } from '../form/appointments-form.resource'; -interface PatientUpcomingAppointmentsProps { +// See VisitFormExtensionState in esm-patient-chart-app +export interface PatientUpcomingAppointmentsProps { + setOnVisitCreatedOrUpdated(onSubmit: (visit: Visit) => Promise); + visitFormOpenedFrom: string; + patientChartConfig?: { + showUpcomingAppointments: boolean; + }; patientUuid: string; - setUpcomingAppointment: (value: Appointment) => void; } +/** + * This is an extension that gets slotted into the patient chart start visit form when + * the appropriate config values are enabled. + * @param param0 + * @returns + */ const PatientUpcomingAppointmentsCard: React.FC = ({ patientUuid, - setUpcomingAppointment, + setOnVisitCreatedOrUpdated, + patientChartConfig, }) => { const { t } = useTranslation(); const startDate = dayjs(new Date().toISOString()).subtract(6, 'month').toISOString(); const headerTitle = t('upcomingAppointments', 'Upcoming appointments'); - const [selectedAppointment, setSelectedAppointment] = useState(null); + const [selectedAppointment, setSelectedAppointment] = useState(null); + const { mutateAppointments } = useMutateAppointments(); const ac = useMemo(() => new AbortController(), []); useEffect(() => () => ac.abort(), [ac]); const { data: appointmentsData, error, isLoading } = usePatientAppointments(patientUuid, startDate, ac); + useEffect(() => { + setOnVisitCreatedOrUpdated(() => { + if (selectedAppointment) { + return changeAppointmentStatus('CheckedIn', selectedAppointment.uuid) + .then(() => { + mutateAppointments(); + showSnackbar({ + isLowContrast: true, + kind: 'success', + subtitle: t('appointmentMarkedChecked', 'Appointment marked as Checked In'), + title: t('appointmentCheckedIn', 'Appointment Checked In'), + }); + }) + .catch((error) => { + showSnackbar({ + title: t('updateError', 'Error updating upcoming appointment'), + kind: 'error', + isLowContrast: false, + subtitle: error?.message, + }); + }); + } else { + return Promise.resolve(); + } + }); + }, [selectedAppointment, mutateAppointments, setOnVisitCreatedOrUpdated, t]); + const todaysAppointments = appointmentsData?.todaysAppointments?.length ? appointmentsData?.todaysAppointments : []; const futureAppointments = appointmentsData?.upcomingAppointments?.length ? appointmentsData?.upcomingAppointments @@ -46,9 +87,12 @@ const PatientUpcomingAppointmentsCard: React.FC { setSelectedAppointment(appointment); - setUpcomingAppointment(appointment); }; + if (!patientChartConfig.showUpcomingAppointments) { + return <>; + } + if (error) { return ; } diff --git a/packages/esm-appointments-app/src/routes.json b/packages/esm-appointments-app/src/routes.json index fbe5d8fac..0e81492e0 100644 --- a/packages/esm-appointments-app/src/routes.json +++ b/packages/esm-appointments-app/src/routes.json @@ -99,7 +99,7 @@ { "name": "patient-upcoming-appointment-widget", "component": "patientUpcomingAppointmentsWidget", - "slot": "upcoming-appointment-slot" + "slot": "visit-form-top-slot" }, { "name": "edit-appointments-form", diff --git a/packages/esm-appointments-app/translations/en.json b/packages/esm-appointments-app/translations/en.json index 4297582f5..d8bf71b55 100644 --- a/packages/esm-appointments-app/translations/en.json +++ b/packages/esm-appointments-app/translations/en.json @@ -7,6 +7,7 @@ "appointmentCancelError": "Error cancelling appointment", "appointmentCancelled": "Appointment Cancelled", "appointmentCancelledSuccessfully": "Appointment cancelled successfully", + "appointmentCheckedIn": "Appointment Checked In", "appointmentColor": "Appointment color", "appointmentConflict": "Appointment conflict", "appointmentEdited": "Appointment edited", @@ -18,6 +19,7 @@ "appointmentEndError": "Error ending appointment", "appointmentFormError": "Error scheduling appointment", "appointmentHistory": "Appointment History", + "appointmentMarkedChecked": "Appointment marked as Checked In", "appointmentMetrics": "Appointment metrics", "appointmentMetricsLoadError": "", "appointmentNoteLabel": "Write an additional note", @@ -158,6 +160,7 @@ "unscheduledAppointments_lower": "unscheduled appointments", "upcoming": "Upcoming", "upcomingAppointments": "Upcoming appointments", + "updateError": "Error updating upcoming appointment", "view": "View", "vitals": "Vitals", "week": "Week", diff --git a/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts b/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts index 587096787..d87634b57 100644 --- a/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts +++ b/packages/esm-service-queues-app/src/active-visits/active-visits-table.resource.ts @@ -193,68 +193,6 @@ export function useServiceQueueEntries(service: string, locationUuid: string) { }; } -export async function postQueueEntry( - visitUuid: string, - queueUuid: string, - patientUuid: string, - priority: string, - status: string, - sortWeight: number, - locationUuid: string, - visitQueueNumberAttributeUuid: string, -) { - const abortController = new AbortController(); - - await Promise.all([generateVisitQueueNumber(locationUuid, visitUuid, queueUuid, visitQueueNumberAttributeUuid)]); - - return openmrsFetch(`${restBaseUrl}/visit-queue-entry`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - signal: abortController.signal, - body: { - visit: { uuid: visitUuid }, - queueEntry: { - status: { - uuid: status, - }, - priority: { - uuid: priority, - }, - queue: { - uuid: queueUuid, - }, - patient: { - uuid: patientUuid, - }, - startedAt: new Date(), - sortWeight: sortWeight, - }, - }, - }); -} - -export async function generateVisitQueueNumber( - location: string, - visitUuid: string, - queueUuid: string, - visitQueueNumberAttributeUuid: string, -) { - const abortController = new AbortController(); - - await openmrsFetch( - `${restBaseUrl}/queue-entry-number?location=${location}&queue=${queueUuid}&visit=${visitUuid}&visitAttributeType=${visitQueueNumberAttributeUuid}`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - signal: abortController.signal, - }, - ); -} - export function serveQueueEntry(servicePointName: string, ticketNumber: string, status: string) { const abortController = new AbortController(); diff --git a/packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx index a15b47c72..6477d75ed 100644 --- a/packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx +++ b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.component.tsx @@ -25,7 +25,7 @@ import { type ConfigObject } from '../config-schema'; import { useQueues } from '../hooks/useQueues'; import { updateQueueEntry } from './active-visits-table.resource'; import { useMutateQueueEntries } from '../hooks/useQueueEntries'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import styles from './change-status-dialog.scss'; interface ChangeStatusDialogProps { diff --git a/packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx index 6f2652567..4f0063a93 100644 --- a/packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx +++ b/packages/esm-service-queues-app/src/active-visits/change-status-dialog.test.tsx @@ -25,7 +25,7 @@ jest.mock('./active-visits-table.resource', () => ({ updateQueueEntry: jest.fn(), })); -jest.mock('../patient-search/hooks/useQueueLocations', () => { +jest.mock('../create-queue-entry/hooks/useQueueLocations', () => { return { useQueueLocations: jest.fn().mockReturnValue({ queueLocations: mockLocations.data?.results.map((location) => ({ ...location, id: location.uuid })), diff --git a/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx index 2cb732b66..8b3bd0698 100644 --- a/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx +++ b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.component.tsx @@ -13,7 +13,7 @@ import { SelectItem, } from '@carbon/react'; import { showSnackbar } from '@openmrs/esm-framework'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import { addProviderToQueueRoom, updateProviderToQueueRoom, diff --git a/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx index 13a09c8e0..5afe9b3fa 100644 --- a/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx +++ b/packages/esm-service-queues-app/src/add-provider-queue-room/add-provider-queue-room.test.tsx @@ -39,7 +39,7 @@ jest.mock('../hooks/useQueues', () => { }; }); -jest.mock('../patient-search/hooks/useQueueLocations', () => ({ +jest.mock('../create-queue-entry/hooks/useQueueLocations', () => ({ useQueueLocations: jest.fn().mockReturnValue({ queueLocations: [ { id: '1GHI12', name: 'Location 1' }, diff --git a/packages/esm-service-queues-app/src/patient-search/patient-search.scss b/packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.scss similarity index 100% rename from packages/esm-service-queues-app/src/patient-search/patient-search.scss rename to packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.scss diff --git a/packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.workspace.tsx b/packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.workspace.tsx new file mode 100644 index 000000000..ebd376e7c --- /dev/null +++ b/packages/esm-service-queues-app/src/create-queue-entry/create-queue-entry.workspace.tsx @@ -0,0 +1,99 @@ +import { Button, DataTableSkeleton } from '@carbon/react'; +import { + ArrowLeftIcon, + ErrorState, + ExtensionSlot, + getPatientName, + PatientBannerContactDetails, + PatientBannerPatientInfo, + PatientBannerToggleContactDetailsButton, + PatientPhoto, + usePatient, + useVisit, + type DefaultWorkspaceProps, +} from '@openmrs/esm-framework'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './create-queue-entry.scss'; +import ExistingVisitFormComponent from './existing-visit-form/existing-visit-form.component'; + +interface PatientSearchProps extends DefaultWorkspaceProps { + selectedPatientUuid: string; + currentServiceQueueUuid?: string; + handleBackToSearchList?: () => void; +} + +export const AddPatientToQueueContext = React.createContext({ + currentServiceQueueUuid: '', +}); + +/** + * + * This is the component that appears when clicking on a search result in the "Add patient to queue" workspace, + */ +const CreateQueueEntryWorkspace: React.FC = ({ + closeWorkspace, + promptBeforeClosing, + selectedPatientUuid, + currentServiceQueueUuid, + handleBackToSearchList, +}) => { + const { t } = useTranslation(); + const { patient } = usePatient(selectedPatientUuid); + const { activeVisit, isLoading, error } = useVisit(selectedPatientUuid); + + const [showContactDetails, setContactDetails] = useState(false); + + const patientName = patient && getPatientName(patient); + + return patient ? ( +
+ +
+
+
+ +
+ + setContactDetails(!showContactDetails)} + /> +
+ {showContactDetails ? ( + + ) : null} +
+
+ +
+ {isLoading ? ( + + ) : error ? ( + + ) : activeVisit ? ( + + ) : ( + + )} +
+
+ ) : null; +}; + +export default CreateQueueEntryWorkspace; diff --git a/packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.component.tsx b/packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.component.tsx new file mode 100644 index 000000000..2c08264b0 --- /dev/null +++ b/packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.component.tsx @@ -0,0 +1,69 @@ +import { Button, ButtonSet, Form, Row } from '@carbon/react'; +import { ExtensionSlot, useLayoutType, type Visit } from '@openmrs/esm-framework'; +import classNames from 'classnames'; +import React, { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutateQueueEntries } from '../../hooks/useQueueEntries'; +import QueueFields from '../queue-fields/queue-fields.component'; +import styles from './existing-visit-form.scss'; + +interface ExistingVisitFormProps { + closeWorkspace: () => void; + visit: Visit; +} + +/** + * This is the form that appears when clicking on a search result in the "Add patient to queue" workspace, + * when the patient already has an active visit. + */ +const ExistingVisitForm: React.FC = ({ visit, closeWorkspace }) => { + const { t } = useTranslation(); + const isTablet = useLayoutType() === 'tablet'; + const [isSubmitting, setIsSubmitting] = useState(false); + + const { mutateQueueEntries } = useMutateQueueEntries(); + const [submitQueueEntry, setSubmitQueueEntry] = useState<(visit: Visit, patientUuid: string) => Promise>(null); + + const handleSubmit = useCallback( + (event) => { + event.preventDefault(); + setIsSubmitting(true); + + submitQueueEntry?.(visit, visit.patient.uuid) + ?.then(() => { + closeWorkspace(); + }) + ?.finally(() => { + setIsSubmitting(false); + }); + }, + [closeWorkspace, submitQueueEntry, visit], + ); + + return visit ? ( + <> + {isTablet && ( + + + + )} +
+ + + + + + + + ) : null; +}; + +export default ExistingVisitForm; diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.scss b/packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.scss similarity index 100% rename from packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.scss rename to packages/esm-service-queues-app/src/create-queue-entry/existing-visit-form/existing-visit-form.scss diff --git a/packages/esm-service-queues-app/src/patient-search/hooks/useQueueLocations.tsx b/packages/esm-service-queues-app/src/create-queue-entry/hooks/useQueueLocations.tsx similarity index 100% rename from packages/esm-service-queues-app/src/patient-search/hooks/useQueueLocations.tsx rename to packages/esm-service-queues-app/src/create-queue-entry/hooks/useQueueLocations.tsx diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.component.tsx similarity index 71% rename from packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx rename to packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.component.tsx index c99aadc2c..fb4ca0922 100644 --- a/packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.component.tsx +++ b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.component.tsx @@ -11,43 +11,86 @@ import { SelectSkeleton, TextInput, } from '@carbon/react'; -import { useConfig, ResponsiveWrapper, useSession } from '@openmrs/esm-framework'; +import { useConfig, ResponsiveWrapper, useSession, type Visit, showSnackbar } from '@openmrs/esm-framework'; import { type ConfigObject } from '../../config-schema'; import { useQueues } from '../../hooks/useQueues'; import { useQueueLocations } from '../hooks/useQueueLocations'; -import { AddPatientToQueueContext } from '../patient-search.workspace'; -import styles from './visit-form-queue-fields.scss'; +import { AddPatientToQueueContext } from '../create-queue-entry.workspace'; +import styles from './queue-fields.scss'; +import { useMutateQueueEntries } from '../../hooks/useQueueEntries'; +import { postQueueEntry } from './queue-fields.resource'; -export interface VisitFormQueueFieldsProps { - setFormFields: (fields: { - queueLocation: string; - service: string; - status: string; - priority: string; - sortWeight: number; - }) => void; +export interface QueueFieldsProps { + setOnSubmit(onSubmit: (visit: Visit) => Promise); } -const VisitFormQueueFields: React.FC = (props) => { - const { setFormFields } = props; +/** + * This component contains form fields for starting a patient's queue entry. + */ +const QueueFields: React.FC = ({ setOnSubmit }) => { const { t } = useTranslation(); const { queueLocations, isLoading: isLoadingQueueLocations } = useQueueLocations(); const { sessionLocation } = useSession(); - const config = useConfig(); + const { + visitQueueNumberAttributeUuid, + concepts: { defaultStatusConceptUuid, defaultPriorityConceptUuid, emergencyPriorityConceptUuid }, + } = useConfig(); const [selectedQueueLocation, setSelectedQueueLocation] = useState(queueLocations[0]?.id); const { queues, isLoading: isLoadingQueues } = useQueues(selectedQueueLocation); - const defaultStatus = config.concepts.defaultStatusConceptUuid; const [selectedService, setSelectedService] = useState(''); const { currentServiceQueueUuid } = useContext(AddPatientToQueueContext); - const [priority, setPriority] = useState(config.concepts.defaultPriorityConceptUuid); + const [priority, setPriority] = useState(defaultPriorityConceptUuid); const priorities = queues.find((q) => q.uuid === selectedService)?.allowedPriorities ?? []; - const [sortWeight, setSortWeight] = useState(0); + const { mutateQueueEntries } = useMutateQueueEntries(); + + const sortWeight = priority === emergencyPriorityConceptUuid ? 1 : 0; useEffect(() => { - if (priority === config.concepts.emergencyPriorityConceptUuid) { - setSortWeight(1); - } - }, [config.concepts.emergencyPriorityConceptUuid, priority]); + setOnSubmit?.((visit: Visit) => { + if (selectedQueueLocation && selectedService && priority) { + return postQueueEntry( + visit.uuid, + selectedService, + visit.patient.uuid, + priority, + defaultStatusConceptUuid, + sortWeight, + selectedQueueLocation, + visitQueueNumberAttributeUuid, + ) + .then(() => { + showSnackbar({ + kind: 'success', + isLowContrast: true, + title: t('addedPatientToQueue', 'Added patient to queue'), + subtitle: t('queueEntryAddedSuccessfully', 'Queue entry added successfully'), + }); + mutateQueueEntries(); + }) + .catch((error) => { + showSnackbar({ + title: t('queueEntryError', 'Error adding patient to the queue'), + kind: 'error', + isLowContrast: false, + subtitle: error?.message, + }); + throw error; + }); + } else { + return Promise.resolve(); + } + }); + }, [ + selectedQueueLocation, + selectedService, + priority, + sortWeight, + defaultStatusConceptUuid, + visitQueueNumberAttributeUuid, + mutateQueueEntries, + setOnSubmit, + t, + ]); useEffect(() => { if (currentServiceQueueUuid) { @@ -61,16 +104,6 @@ const VisitFormQueueFields: React.FC = (props) => { } }, [queueLocations, sessionLocation.uuid]); - useEffect(() => { - setFormFields({ - queueLocation: selectedQueueLocation, - service: selectedService, - status: defaultStatus, - priority, - sortWeight, - }); - }, [selectedQueueLocation, selectedService, defaultStatus, priority, sortWeight, setFormFields]); - return (
@@ -159,7 +192,7 @@ const VisitFormQueueFields: React.FC = (props) => { className={styles.radioButtonWrapper} name="priority" id="priority" - defaultSelected={config.concepts.defaultPriorityConceptUuid} + defaultSelected={defaultPriorityConceptUuid} onChange={(uuid) => setPriority(uuid)}> {priorities.map(({ uuid, display }) => ( @@ -182,4 +215,4 @@ const VisitFormQueueFields: React.FC = (props) => { ); }; -export default VisitFormQueueFields; +export default QueueFields; diff --git a/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.resource.ts b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.resource.ts new file mode 100644 index 000000000..897547360 --- /dev/null +++ b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.resource.ts @@ -0,0 +1,63 @@ +import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; + +export async function generateVisitQueueNumber( + location: string, + visitUuid: string, + queueUuid: string, + visitQueueNumberAttributeUuid: string, +) { + const abortController = new AbortController(); + + await openmrsFetch( + `${restBaseUrl}/queue-entry-number?location=${location}&queue=${queueUuid}&visit=${visitUuid}&visitAttributeType=${visitQueueNumberAttributeUuid}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + signal: abortController.signal, + }, + ); +} + +export async function postQueueEntry( + visitUuid: string, + queueUuid: string, + patientUuid: string, + priority: string, + status: string, + sortWeight: number, + locationUuid: string, + visitQueueNumberAttributeUuid: string, +) { + const abortController = new AbortController(); + + await Promise.all([generateVisitQueueNumber(locationUuid, visitUuid, queueUuid, visitQueueNumberAttributeUuid)]); + + return openmrsFetch(`${restBaseUrl}/visit-queue-entry`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + signal: abortController.signal, + body: { + visit: { uuid: visitUuid }, + queueEntry: { + status: { + uuid: status, + }, + priority: { + uuid: priority, + }, + queue: { + uuid: queueUuid, + }, + patient: { + uuid: patientUuid, + }, + startedAt: new Date(), + sortWeight: sortWeight, + }, + }, + }); +} diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.scss b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.scss similarity index 100% rename from packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.scss rename to packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.scss diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.test.tsx similarity index 59% rename from packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx rename to packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.test.tsx index 8cada9c8a..9e6557439 100644 --- a/packages/esm-service-queues-app/src/patient-search/visit-form-queue-fields/visit-form-queue-fields.test.tsx +++ b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/queue-fields.test.tsx @@ -2,10 +2,18 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; -import { getDefaultsFromConfigSchema, useConfig, useLayoutType, useSession } from '@openmrs/esm-framework'; +import { + type FetchResponse, + getDefaultsFromConfigSchema, + useConfig, + useLayoutType, + useSession, + type Visit, +} from '@openmrs/esm-framework'; import { configSchema, type ConfigObject } from '../../config-schema'; -import { mockSession } from '__mocks__'; -import VisitFormQueueFields from './visit-form-queue-fields.component'; +import { mockSession, mockVisitAlice } from '__mocks__'; +import QueueFields from './queue-fields.component'; +import { postQueueEntry } from './queue-fields.resource'; const mockUseConfig = jest.mocked(useConfig); const mockUseLayoutType = jest.mocked(useLayoutType); @@ -32,7 +40,14 @@ jest.mock('../../hooks/useQueues', () => { }; }); -describe('VisitFormQueueFields', () => { +jest.mock('./queue-fields.resource', () => { + return { + postQueueEntry: jest.fn(), + }; +}); +const mockPostQueueEntry = jest.mocked(postQueueEntry).mockResolvedValue({} as FetchResponse); + +describe('QueueFields', () => { beforeEach(() => { mockUseLayoutType.mockReturnValue('small-desktop'); mockUseSession.mockReturnValue(mockSession.data); @@ -43,27 +58,31 @@ describe('VisitFormQueueFields', () => { it('renders the form fields and returns the set values', async () => { const user = userEvent.setup(); - const setFormFields = jest.fn(); - render(); + let onSubmit: (visit: Visit) => Promise = null; + const setOnSubmit = (callback) => (onSubmit = callback); + render(); expect(screen.getByLabelText('Select a queue location')).toBeInTheDocument(); expect(screen.getByLabelText('Select a service')).toBeInTheDocument(); expect(screen.getByLabelText('Sort weight')).toBeInTheDocument(); + const queueUuid = 'e2ec9cf0-ec38-4d2b-af6c-59c82fa30b90'; const serviceSelect = screen.getByLabelText('Select a service').closest('select'); - await user.selectOptions(serviceSelect, 'e2ec9cf0-ec38-4d2b-af6c-59c82fa30b90'); + await user.selectOptions(serviceSelect, queueUuid); expect(screen.getByText('Priority')).toBeInTheDocument(); expect(screen.getByText('High')).toBeInTheDocument(); - expect(setFormFields).toHaveBeenCalledWith({ - queueLocation: '1', - service: 'e2ec9cf0-ec38-4d2b-af6c-59c82fa30b90', - // The status and priority are the defaults that come from the config schema. - // They are selected even though they are not 'allowed' for the queue. - status: '51ae5e4d-b72b-4912-bf31-a17efb690aeb', - priority: 'f4620bfa-3625-4883-bd3f-84c2cce14470', - sortWeight: 0, - }); + await onSubmit(mockVisitAlice); + expect(mockPostQueueEntry).toHaveBeenCalledWith( + mockVisitAlice.uuid, + queueUuid, // queueUuid + mockVisitAlice.patient.uuid, + 'f4620bfa-3625-4883-bd3f-84c2cce14470', // priority + '51ae5e4d-b72b-4912-bf31-a17efb690aeb', // status + 0, // sortWeight + '1', // locationUuid + null, // visitQueueNumberAttributeUuid + ); }); }); diff --git a/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/visit-form-queue-fields.extension.tsx b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/visit-form-queue-fields.extension.tsx new file mode 100644 index 000000000..49433f40b --- /dev/null +++ b/packages/esm-service-queues-app/src/create-queue-entry/queue-fields/visit-form-queue-fields.extension.tsx @@ -0,0 +1,28 @@ +import { type Visit } from '@openmrs/esm-framework'; +import React from 'react'; +import QueueFields from './queue-fields.component'; + +// See VisitFormExtensionState in esm-patient-chart-app +export interface VisitFormQueueFieldsProps { + setOnVisitCreatedOrUpdated(onSubmit: (visit: Visit) => Promise); + visitFormOpenedFrom: string; + patientChartConfig?: { + showServiceQueueFields: boolean; + }; + patientUuid: string; +} + +/** + * This extension contains form fields for starting a patient's queue entry. + * It is used slotted into the patient-chart's start visit form + */ +const VisitFormQueueFields: React.FC = (props) => { + const { setOnVisitCreatedOrUpdated, visitFormOpenedFrom, patientChartConfig } = props; + if (patientChartConfig.showServiceQueueFields || visitFormOpenedFrom == 'service-queues-add-patient') { + return ; + } else { + return <>; + } +}; + +export default VisitFormQueueFields; diff --git a/packages/esm-service-queues-app/src/index.ts b/packages/esm-service-queues-app/src/index.ts index d2e76c388..48857a0dd 100644 --- a/packages/esm-service-queues-app/src/index.ts +++ b/packages/esm-service-queues-app/src/index.ts @@ -10,7 +10,7 @@ import outpatientSideNavComponent from './side-menu/side-menu.component'; import homeDashboardComponent from './home.component'; import patientInfoBannerSlotComponent from './patient-info/patient-info.component'; import pastVisitSummaryComponent from './past-visit/past-visit.component'; -import VisitFormQueueFields from './patient-search/visit-form-queue-fields/visit-form-queue-fields.component'; +import VisitFormQueueFields from './create-queue-entry/queue-fields/visit-form-queue-fields.extension'; export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); @@ -149,11 +149,13 @@ export const addNewQueueServiceRoomWorkspace = getAsyncLifecycle( export const visitFormQueueFields = getSyncLifecycle(VisitFormQueueFields, options); -// t('searchPatient', 'Search Patient') -export const patientSearchWorkspace = getAsyncLifecycle(() => import('./patient-search/patient-search.workspace'), { - featureName: 'service-queues-patient-search', - moduleName, -}); +export const createQueueEntryWorkspace = getAsyncLifecycle( + () => import('./create-queue-entry/create-queue-entry.workspace'), + { + featureName: 'create-queue-entry-workspace', + moduleName, + }, +); export const activeVisitsRowActions = getAsyncLifecycle( () => import('./active-visits/active-visits-row-actions.component'), diff --git a/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx index f6f2c6919..b79f8bb98 100644 --- a/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx +++ b/packages/esm-service-queues-app/src/patient-queue-header/patient-queue-header.component.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Dropdown } from '@carbon/react'; import { useConfig, useSession, PageHeader, PageHeaderContent, ServiceQueuesPictogram } from '@openmrs/esm-framework'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import { updateSelectedQueueLocationUuid, updateSelectedQueueLocationName, diff --git a/packages/esm-service-queues-app/src/patient-search/empty-data-illustration.component.tsx b/packages/esm-service-queues-app/src/patient-search/empty-data-illustration.component.tsx deleted file mode 100644 index 42e93e03e..000000000 --- a/packages/esm-service-queues-app/src/patient-search/empty-data-illustration.component.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; - -const EmptyDataIllustration = ({ width = '61', height = '59' }) => { - return ( - - Empty data illustration - - - - - - - - - - - - - - ); -}; - -export default EmptyDataIllustration; diff --git a/packages/esm-service-queues-app/src/patient-search/hooks/useActivePatientEnrollment.tsx b/packages/esm-service-queues-app/src/patient-search/hooks/useActivePatientEnrollment.tsx deleted file mode 100644 index b76887ab5..000000000 --- a/packages/esm-service-queues-app/src/patient-search/hooks/useActivePatientEnrollment.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useMemo } from 'react'; -import useSWR from 'swr'; -import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; -import { type PatientProgram } from '../../types'; -import uniqBy from 'lodash-es/uniqBy'; - -export const useActivePatientEnrollment = (patientUuid: string) => { - const customRepresentation = `custom:(uuid,display,program,dateEnrolled,dateCompleted,location:(uuid,display))`; - const apiUrl = `${restBaseUrl}/programenrollment?patient=${patientUuid}&v=${customRepresentation}`; - - const { data, error, isLoading } = useSWR<{ data: { results: Array } }>( - patientUuid ? apiUrl : null, - openmrsFetch, - ); - - const activePatientEnrollment = useMemo( - () => - data?.data.results - .sort((a, b) => (b.dateEnrolled > a.dateEnrolled ? 1 : -1)) - .filter((enrollment) => enrollment.dateCompleted === null) ?? [], - [data?.data.results], - ); - - return { - activePatientEnrollment: uniqBy(activePatientEnrollment, (program) => program?.program?.uuid), - error, - isLoading, - }; -}; diff --git a/packages/esm-service-queues-app/src/patient-search/hooks/useDefaultLocation.ts b/packages/esm-service-queues-app/src/patient-search/hooks/useDefaultLocation.ts deleted file mode 100644 index 59be7209c..000000000 --- a/packages/esm-service-queues-app/src/patient-search/hooks/useDefaultLocation.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { type FetchResponse, openmrsFetch, useConfig } from '@openmrs/esm-framework'; -import useSWRImmutable from 'swr/immutable'; - -export const useDefaultLoginLocation = () => { - const config = useConfig(); - const apiUrl = config.defaultFacilityUrl; - const { data, error, isLoading } = useSWRImmutable(apiUrl, openmrsFetch); - - return { - defaultFacility: data ? data?.data : null, - isLoading: isLoading, - error, - }; -}; diff --git a/packages/esm-service-queues-app/src/patient-search/hooks/useRecommendedVisitTypes.tsx b/packages/esm-service-queues-app/src/patient-search/hooks/useRecommendedVisitTypes.tsx deleted file mode 100644 index 9adaaa99e..000000000 --- a/packages/esm-service-queues-app/src/patient-search/hooks/useRecommendedVisitTypes.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import useSWR from 'swr'; -import { openmrsFetch, useConfig } from '@openmrs/esm-framework'; -import { type ConfigObject } from '../../config-schema'; -import { useMemo } from 'react'; - -interface EnrollmentVisitType { - dataDependencies: Array; - enrollmentOptions: object; - incompatibleWith: Array; - name: string; - visitTypes: { allowed: Array; disallowed: Array }; -} - -export const useRecommendedVisitTypes = ( - patientUuid: string, - enrollmentUuid: string, - programUuid: string, - locationUuid: string, -) => { - const { visitTypeResourceUrl, showRecommendedVisitTypeTab } = useConfig(); - - const apiUrl = `${visitTypeResourceUrl}${patientUuid}/program/${programUuid}/enrollment/${enrollmentUuid}?intendedLocationUuid=${locationUuid}`; - - const { data, error, isLoading } = useSWR<{ data: EnrollmentVisitType }>( - showRecommendedVisitTypeTab && patientUuid && enrollmentUuid && programUuid ? apiUrl : null, - openmrsFetch, - ); - - const recommendedVisitTypes = useMemo(() => data?.data?.visitTypes?.allowed.map(mapToVisitType) ?? [], [data]); - return { recommendedVisitTypes, error, isLoading }; -}; - -const mapToVisitType = (visitType) => { - return { ...visitType, display: visitType.name }; -}; diff --git a/packages/esm-service-queues-app/src/patient-search/hooks/useScheduledVisits.ts b/packages/esm-service-queues-app/src/patient-search/hooks/useScheduledVisits.ts deleted file mode 100644 index 161b5ffe4..000000000 --- a/packages/esm-service-queues-app/src/patient-search/hooks/useScheduledVisits.ts +++ /dev/null @@ -1,52 +0,0 @@ -import useSWR from 'swr'; -import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; -import dayjs from 'dayjs'; -import { type AppointmentsFetchResponse } from '../../types'; - -const fetcher = (appointmentsSearchUrl: string, patientUuid: string) => { - const abortController = new AbortController(); - let startDate = dayjs(new Date().toISOString()).subtract(6, 'month').toISOString(); - - return openmrsFetch(appointmentsSearchUrl, { - method: 'POST', - signal: abortController.signal, - headers: { - 'Content-Type': 'application/json', - }, - body: { - patientUuid: patientUuid, - startDate: startDate, - }, - }); -}; - -export function useScheduledVisits(patientUuid: string) { - const appointmentsSearchUrl = `${restBaseUrl}/appointments/search`; - - const { data, error, isLoading } = useSWR( - patientUuid ? [appointmentsSearchUrl, patientUuid] : null, - ([appointmentsSearchUrl, patientUuid]) => fetcher(appointmentsSearchUrl, patientUuid as string), - ); - - const appointments = data?.data?.length - ? data.data.sort((a, b) => (b.startDateTime > a.startDateTime ? 1 : -1)) - : null; - - // visits + or - 7 days before visit date - const recentVisits = appointments?.filter( - (appointment) => - dayjs((appointment.startDateTime / 1000) * 1000).isBefore(dayjs().add(7, 'day')) || - dayjs((appointment.startDateTime / 1000) * 1000).isBefore(dayjs().subtract(7, 'day')), - ); - - // visits past 7 days - const futureVisits = appointments?.filter((appointment) => - dayjs((appointment.startDateTime / 1000) * 1000).isAfter(dayjs().add(7, 'day')), - ); - - return { - appointments: data ? { recentVisits, futureVisits } : null, - error, - isLoading, - }; -} diff --git a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.component.tsx b/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.component.tsx deleted file mode 100644 index 37fcdb36b..000000000 --- a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.component.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import dayjs from 'dayjs'; -import head from 'lodash-es/head'; -import { useTranslation } from 'react-i18next'; -import { - Button, - ButtonSet, - ContentSwitcher, - DataTableSkeleton, - InlineLoading, - InlineNotification, - RadioTile, - Switch, - TileGroup, -} from '@carbon/react'; -import { - formatDatetime, - type NewVisitPayload, - parseDate, - saveVisit, - showSnackbar, - toDateObjectStrict, - toOmrsIsoString, - useConfig, - useLayoutType, - useLocations, - useSession, - useVisit, - useVisitTypes, -} from '@openmrs/esm-framework'; -import { type Appointment, SearchTypes } from '../types'; -import { postQueueEntry } from './visit-form/queue.resource'; -import { convertTime12to24 } from '../helpers/time-helpers'; -import { useQueueLocations } from './hooks/useQueueLocations'; -import { useQueues } from '../hooks/useQueues'; -import { useMutateQueueEntries } from '../hooks/useQueueEntries'; -import { type ConfigObject } from '../config-schema'; -import styles from './patient-scheduled-visits.scss'; - -enum visitType { - RECENT = 'Recent', - FUTURE = 'Future', -} - -const ScheduledVisitsForVisitType: React.FC<{ - visits; - visitType; - scheduledVisitHeader; - patientUuid; - closeWorkspace: () => void; -}> = ({ visits, scheduledVisitHeader, patientUuid, closeWorkspace }) => { - const { t } = useTranslation(); - const [visitsIndex, setVisitsIndex] = useState(0); - const [hasPriority, setHasPriority] = useState(false); - const [userLocation, setUserLocation] = useState(''); - const locations = useLocations(); - const session = useSession(); - const { queues, isLoading: isLoadingQueues } = useQueues(userLocation); - const { mutateQueueEntries } = useMutateQueueEntries(); - const [isSubmitting, setIsSubmitting] = useState(false); - const timeFormat = new Date().getHours() >= 12 ? 'PM' : 'AM'; - const visitDate = useMemo(() => new Date(), []); - const visitTime = dayjs(new Date()).format('hh:mm'); - const [appointment, setAppointment] = useState(); - const [patientId, setPatientId] = useState(''); - const allVisitTypes = useVisitTypes(); - const { currentVisit } = useVisit(patientUuid); - const config = useConfig(); - const visitQueueNumberAttributeUuid = config.visitQueueNumberAttributeUuid; - const { queueLocations } = useQueueLocations(); - const selectedQueueLocation = queueLocations[0]?.id; - - // TODO: This needs fixing, we cannot just take the first queue and assume that is what is wanted - const service = head(queues)?.uuid; - const defaultStatus = config.concepts.defaultStatusConceptUuid; - const priorities = queues.find((q) => q.uuid === service)?.allowedPriorities ?? []; - - useEffect(() => { - if (!userLocation && session?.sessionLocation !== null) { - setUserLocation(session?.sessionLocation?.uuid); - } else if (!userLocation && locations) { - setUserLocation(head(locations)?.uuid); - } - }, [session, locations, userLocation]); - - const handleSubmit = useCallback( - (priority) => { - setIsSubmitting(true); - const [hours, minutes] = convertTime12to24(visitTime, timeFormat); - const visitType = [...allVisitTypes].shift().uuid; - - const payload: NewVisitPayload = { - patient: patientId, - startDatetime: toDateObjectStrict( - toOmrsIsoString( - new Date(dayjs(visitDate).year(), dayjs(visitDate).month(), dayjs(visitDate).date(), hours, minutes), - ), - ), - visitType: visitType, - location: userLocation, - }; - - if (currentVisit) { - showSnackbar({ - title: t('startVisitError', 'Error starting visit'), - kind: 'error', - isLowContrast: false, - subtitle: t('patientHasActiveVisit', 'The patient already has an active visit'), - }); - setIsSubmitting(false); - } else { - const abortController = new AbortController(); - - saveVisit(payload, abortController) - .then((response) => { - postQueueEntry( - response.data.uuid, - patientId, - priority, - defaultStatus, - service, - appointment, - selectedQueueLocation, - visitQueueNumberAttributeUuid, - ) - .then(() => { - showSnackbar({ - kind: 'success', - title: t('startAVisit', 'Start a visit'), - subtitle: t( - 'startVisitQueueSuccessfully', - 'Patient has been added to active visits list and queue.', - `${hours} : ${minutes}`, - ), - }); - closeWorkspace(); - setIsSubmitting(false); - mutateQueueEntries(); - }) - .catch((error) => { - showSnackbar({ - title: t('queueEntryError', 'Error adding patient to the queue'), - kind: 'error', - isLowContrast: false, - subtitle: error?.message, - }); - setIsSubmitting(false); - }); - }) - .catch((error) => { - showSnackbar({ - title: t('startVisitError', 'Error starting visit'), - kind: 'error', - isLowContrast: false, - subtitle: error?.message, - }); - setIsSubmitting(false); - }); - } - }, - [ - allVisitTypes, - appointment, - closeWorkspace, - currentVisit, - defaultStatus, - mutateQueueEntries, - patientId, - selectedQueueLocation, - service, - t, - timeFormat, - userLocation, - visitDate, - visitQueueNumberAttributeUuid, - visitTime, - ], - ); - - if (visits) { - return ( -
-

{scheduledVisitHeader}

- {visits?.length > 0 ? ( - - {visits?.map((visit, ind) => { - return ( - { - setHasPriority(true); - setVisitsIndex(ind); - setPatientId(visit?.patient?.uuid); - setAppointment(visit); - }}> -
-

{visit.service?.name}

-

- {' '} - {formatDatetime(parseDate(visit?.startDateTime))} · {visit.location?.name}{' '} -

- - {!visit.service ? ( - - ) : isLoadingQueues ? null : !priorities?.length ? ( - - ) : hasPriority && ind == visitsIndex ? ( - { - handleSubmit(e.name); - }}> - {priorities?.length > 0 - ? priorities.map(({ uuid, display }) => { - return ; - }) - : null} - - ) : null} - {hasPriority && ind == visitsIndex && isSubmitting ? ( - - ) : null} -
-
- ); - })} -
- ) : ( -
-

{t('noAppointmentsFound', 'No appointments found')}

-
- )} -
- ); - } - return ( -
-

{scheduledVisitHeader}

-
- ); -}; - -interface PatientScheduledVisitsProps { - appointments: { recentVisits: Appointment[]; futureVisits: Appointment[] }; - toggleSearchType: (searchMode: SearchTypes) => void; - patientUuid: string; - closeWorkspace: () => void; -} - -const PatientScheduledVisits: React.FC = ({ - appointments, - toggleSearchType, - patientUuid, - closeWorkspace, -}) => { - const { t } = useTranslation(); - const isTablet = useLayoutType() === 'tablet'; - - return ( -
- - - -
{t('orInProperFormat', 'Or')}
- -
- -
- - - - - -
- ); -}; - -export default PatientScheduledVisits; diff --git a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.scss b/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.scss deleted file mode 100644 index c906a48ff..000000000 --- a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.scss +++ /dev/null @@ -1,131 +0,0 @@ -@use '@carbon/layout'; -@use '@carbon/type'; -@use '@openmrs/esm-styleguide/src/vars' as *; - -.container { - background-color: $ui-background; - display: flex; - flex-direction: column; - justify-content: space-between; - height: 100%; -} - -.grid { - padding: 0; -} - -.visitTile { - margin: layout.$spacing-05 0; - - &:global(.cds--tile--is-selected:focus) { - background-color: $color-blue-10; - color: $color-blue-10; - border: 1px solid $color-blue-60-2; - outline: none; - } - - &:global(.cds--tile--is-selected:hover) { - background-color: $color-blue-10; - color: $color-blue-10; - border: 1px solid $color-blue-60-2; - outline: none; - } - - &:global(.cds--tile--is-selected:hover .cds--tile__checkmark svg) { - fill: $color-blue-60-2; - } -} - -.recentlyScheduledVisitsContainer { - margin-top: layout.$spacing-06; - margin-bottom: layout.$spacing-10; -} - -.row { - margin: 0 layout.$spacing-05 layout.$spacing-05 layout.$spacing-05; -} - -.backButton { - align-items: center; - display: flex; - justify-content: flex-start; - margin: layout.$spacing-03 0; - padding: 0; - @include type.type-style('body-compact-01'); - - button { - display: flex; - - svg { - order: 1; - margin-right: layout.$spacing-03; - margin-left: 0 !important; - } - - span { - order: 2; - } - } -} - -.heading { - @include type.type-style('body-02'); - color: $text-02; - margin-left: 1.2rem; -} - -.primaryText { - @include type.type-style('heading-compact-02'); -} - -.secondaryText { - @include type.type-style('body-01'); - color: $text-02; -} - -.button { - height: layout.$spacing-10; - display: flex; - align-content: flex-start; - align-items: baseline; - min-width: 50%; -} - -.text-divider { - display: flex; - align-items: center; - margin: layout.$spacing-08 25%; - @include type.type-style('heading-03'); - color: $text-02; -} - -.text-divider::before, -.text-divider::after { - content: ''; - height: 1px; - background-color: $text-03; - flex-grow: 1; -} - -.text-divider::before { - margin-right: layout.$spacing-05; -} - -.text-divider::after { - margin-left: layout.$spacing-05; -} - -.buttonContainer { - display: flex; - align-items: center; - justify-content: center; - margin-bottom: layout.$spacing-03; -} - -.prioritySwitcher { - margin: layout.$spacing-03 0 layout.$spacing-03; -} - -.emptyAppointment { - margin: layout.$spacing-05; -} diff --git a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.test.tsx b/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.test.tsx deleted file mode 100644 index 0f8f771f6..000000000 --- a/packages/esm-service-queues-app/src/patient-search/patient-scheduled-visits.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { screen } from '@testing-library/react'; -import { getDefaultsFromConfigSchema, useConfig, useLocations, useSession } from '@openmrs/esm-framework'; -import { renderWithSwr } from 'tools'; -import { type ConfigObject, configSchema } from '../config-schema'; -import { mockLocations, mockPatient, mockPatientsVisits, mockSession } from '__mocks__'; -import PatientScheduledVisits from './patient-scheduled-visits.component'; - -const mockUseConfig = jest.mocked(useConfig); -const mockToggleSearchType = jest.fn(); -const mockUseLocations = useLocations as jest.Mock; -const mockUseSession = jest.mocked(useSession); - -const defaultProps = { - appointments: { recentVisits: mockPatientsVisits.recentVisits, futureVisits: [] }, - closePanel: () => false, - closeWorkspace: jest.fn(), - patientUuid: mockPatient.uuid, - toggleSearchType: mockToggleSearchType, -}; - -describe('ScheduledVisits', () => { - beforeEach(() => { - mockUseConfig.mockReturnValue({ - ...getDefaultsFromConfigSchema(configSchema), - concepts: {}, - } as ConfigObject); - mockUseLocations.mockReturnValue(mockLocations.data.results); - mockUseSession.mockReturnValue(mockSession.data); - }); - - it('should display recent and future scheduled visits', async () => { - renderWithSwr(); - - expect(screen.getByText(/Cardiology Consultation 1/i)).toBeInTheDocument(); - expect(screen.getByText(/08-Aug-2022, 02:56 PM · 10 Engineer VCT/i)).toBeInTheDocument(); - expect(screen.getByText(/No appointments found/i)).toBeInTheDocument(); - }); -}); diff --git a/packages/esm-service-queues-app/src/patient-search/patient-search.workspace.tsx b/packages/esm-service-queues-app/src/patient-search/patient-search.workspace.tsx deleted file mode 100644 index f272c9f1a..000000000 --- a/packages/esm-service-queues-app/src/patient-search/patient-search.workspace.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import isNil from 'lodash-es/isNil'; -import { useTranslation } from 'react-i18next'; -import { Button, DataTableSkeleton } from '@carbon/react'; -import { - ArrowLeftIcon, - ErrorState, - getPatientName, - PatientBannerContactDetails, - PatientBannerPatientInfo, - PatientBannerToggleContactDetailsButton, - PatientPhoto, - type DefaultWorkspaceProps, - usePatient, - useVisit, -} from '@openmrs/esm-framework'; -import { SearchTypes } from '../types'; -import { useScheduledVisits } from './hooks/useScheduledVisits'; -import ExistingVisitFormComponent from './visit-form/existing-visit-form.component'; -import PatientScheduledVisits from './patient-scheduled-visits.component'; -import VisitForm from './visit-form/visit-form.component'; -import styles from './patient-search.scss'; - -interface PatientSearchProps extends DefaultWorkspaceProps { - selectedPatientUuid: string; - currentServiceQueueUuid?: string; - handleBackToSearchList?: () => void; -} - -export const AddPatientToQueueContext = React.createContext({ - currentServiceQueueUuid: '', -}); - -const PatientSearch: React.FC = ({ - closeWorkspace, - selectedPatientUuid, - currentServiceQueueUuid, - handleBackToSearchList, -}) => { - const { t } = useTranslation(); - const { patient } = usePatient(selectedPatientUuid); - const { activeVisit } = useVisit(selectedPatientUuid); - const { appointments, isLoading, error } = useScheduledVisits(selectedPatientUuid); - - const [searchType, setSearchType] = useState(SearchTypes.SCHEDULED_VISITS); - const [showContactDetails, setContactDetails] = useState(false); - - const hasAppointments = !(isNil(appointments?.futureVisits) && isNil(appointments?.recentVisits)); - const patientName = patient && getPatientName(patient); - - const backButtonDescription = - searchType === SearchTypes.VISIT_FORM && hasAppointments - ? t('backToScheduledVisits', 'Back to scheduled visits') - : t('backToSearchResults', 'Back to search results'); - - const toggleSearchType = (searchType: SearchTypes) => { - setSearchType(searchType); - }; - - const handleBackToAction = () => { - if (searchType === SearchTypes.VISIT_FORM && hasAppointments) { - setSearchType(SearchTypes.SCHEDULED_VISITS); - } else { - setSearchType(SearchTypes.SEARCH_RESULTS); - } - }; - - useEffect(() => { - if (searchType === SearchTypes.SCHEDULED_VISITS && appointments && !hasAppointments) { - setSearchType(SearchTypes.VISIT_FORM); - } - }, [appointments, hasAppointments, searchType]); - - useEffect(() => { - if (searchType === SearchTypes.SEARCH_RESULTS) { - handleBackToSearchList && handleBackToSearchList(); - } - }, [searchType, handleBackToSearchList]); - - return patient ? ( -
- -
-
-
- -
- - setContactDetails(!showContactDetails)} - /> -
- {showContactDetails ? ( - - ) : null} -
-
- -
- {activeVisit ? ( - - ) : ( - <> - {error ? ( - - ) : null} - - {isLoading && !error ? ( - - ) : searchType === SearchTypes.SCHEDULED_VISITS && hasAppointments ? ( - - ) : searchType === SearchTypes.VISIT_FORM ? ( - - ) : null} - - )} -
-
- ) : null; -}; - -export default PatientSearch; diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/existing-visit-form.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/existing-visit-form.component.tsx deleted file mode 100644 index 3264e6e65..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/existing-visit-form.component.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import classNames from 'classnames'; -import { Button, ButtonSet, Form, Row } from '@carbon/react'; -import { useTranslation } from 'react-i18next'; -import { - type ConfigObject, - ExtensionSlot, - showSnackbar, - useConfig, - useLayoutType, - type Visit, -} from '@openmrs/esm-framework'; -import { postQueueEntry } from '../../active-visits/active-visits-table.resource'; -import { useMutateQueueEntries } from '../../hooks/useQueueEntries'; -import VisitFormQueueFields from '../visit-form-queue-fields/visit-form-queue-fields.component'; -import styles from './visit-form.scss'; - -interface ExistingVisitFormProps { - closeWorkspace: () => void; - visit: Visit; -} - -const ExistingVisitForm: React.FC = ({ visit, closeWorkspace }) => { - const { t } = useTranslation(); - const isTablet = useLayoutType() === 'tablet'; - const [isSubmitting, setIsSubmitting] = useState(false); - - const config = useConfig(); - const visitQueueNumberAttributeUuid = config.visitQueueNumberAttributeUuid; - const { mutateQueueEntries } = useMutateQueueEntries(); - const [{ service, priority, status, sortWeight, queueLocation }, setVisitFormFields] = useState({ - service: null, - priority: null, - status: null, - sortWeight: null, - queueLocation: null, - }); - - const handleSubmit = useCallback( - (event) => { - event.preventDefault(); - - setIsSubmitting(true); - - postQueueEntry( - visit.uuid, - service, - visit.patient.uuid, - priority, - status, - sortWeight, - queueLocation, - visitQueueNumberAttributeUuid, - ).then( - ({ status }) => { - if (status === 201) { - showSnackbar({ - kind: 'success', - isLowContrast: true, - title: t('addPatientToQueue', 'Add patient to queue'), - subtitle: t('queueEntryAddedSuccessfully', 'Queue entry added successfully'), - }); - closeWorkspace(); - setIsSubmitting(false); - mutateQueueEntries(); - } - }, - (error) => { - let subtitle = error?.message; - const err = error?.responseBody?.error?.message; - if (err && err === '[queue.entry.duplicate.patient]') { - subtitle = t('patientAlreadyInQueue', 'Patient is already in the queue'); - } - showSnackbar({ - title: t('queueEntryError', 'Error adding patient to the queue'), - kind: 'error', - subtitle, - }); - setIsSubmitting(false); - }, - ); - }, - [ - closeWorkspace, - mutateQueueEntries, - priority, - queueLocation, - service, - sortWeight, - status, - t, - visit.patient.uuid, - visit.uuid, - visitQueueNumberAttributeUuid, - ], - ); - - return visit ? ( - <> - {isTablet && ( - - - - )} -
- - - - - - - - ) : null; -}; - -export default ExistingVisitForm; diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts b/packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts deleted file mode 100644 index a976d93be..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/queue.resource.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { openmrsFetch, restBaseUrl, toDateObjectStrict, toOmrsIsoString } from '@openmrs/esm-framework'; -import { generateVisitQueueNumber } from '../../active-visits/active-visits-table.resource'; -import { type Appointment } from '../../types'; - -export async function postQueueEntry( - visitUuid: string, - patientUuid: string, - priority: string, - status: string, - queueServiceUuid: string, - appointment: Appointment, - locationUuid: string, - visitQueueNumberAttributeUuid: string, -) { - const abortController = new AbortController(); - - await Promise.all([ - saveAppointment(appointment), - generateVisitQueueNumber(locationUuid, visitUuid, queueServiceUuid, visitQueueNumberAttributeUuid), - ]); - - return openmrsFetch(`${restBaseUrl}/visit-queue-entry`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - signal: abortController.signal, - body: { - visit: visitUuid, - queueEntry: { - status: status, - priority: priority, - queue: queueServiceUuid, - patient: patientUuid, - startedAt: new Date(), - }, - }, - }); -} - -export async function saveAppointment(appointment: Appointment) { - const abortController = new AbortController(); - - await openmrsFetch(`${restBaseUrl}/appointment`, { - method: 'POST', - signal: abortController.signal, - headers: { - 'Content-Type': 'application/json', - }, - body: { - patientUuid: appointment?.patient.uuid, - serviceUuid: appointment?.service?.uuid, - startDateTime: appointment?.startDateTime, - endDateTime: appointment?.endDateTime, - appointmentKind: appointment?.appointmentKind, - locationUuid: appointment?.location?.uuid, - comments: appointment?.comments, - status: 'CheckedIn', - appointmentNumber: appointment?.appointmentNumber, - uuid: appointment?.uuid, - providerUuid: appointment?.provider?.uuid, - }, - }); -} diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx deleted file mode 100644 index 2c07a1977..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-form.component.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import dayjs from 'dayjs'; -import { - Button, - ButtonSet, - ContentSwitcher, - DatePicker, - DatePickerInput, - Form, - FormGroup, - InlineNotification, - RadioButton, - RadioButtonGroup, - Row, - Select, - SelectItem, - Stack, - Switch, - TimePicker, - TimePickerSelect, -} from '@carbon/react'; -import { useTranslation } from 'react-i18next'; -import { - ExtensionSlot, - ResponsiveWrapper, - saveVisit, - showSnackbar, - toDateObjectStrict, - toOmrsIsoString, - useConfig, - useLayoutType, - useLocations, - useSession, -} from '@openmrs/esm-framework'; -import { RecommendedVisitTypeSelector, VisitTypeSelector } from './visit-type-selector.component'; -import { postQueueEntry } from '../../active-visits/active-visits-table.resource'; -import { type amPm, convertTime12to24 } from '../../helpers/time-helpers'; -import { useActivePatientEnrollment } from '../hooks/useActivePatientEnrollment'; -import { type NewVisitPayload, type PatientProgram } from '../../types'; -import styles from './visit-form.scss'; -import { useDefaultLoginLocation } from '../hooks/useDefaultLocation'; -import isEmpty from 'lodash-es/isEmpty'; -import { useMutateQueueEntries } from '../../hooks/useQueueEntries'; -import { type ConfigObject } from '../../config-schema'; -import { datePickerFormat, datePickerPlaceHolder } from '../../constants'; -import VisitFormQueueFields from '../visit-form-queue-fields/visit-form-queue-fields.component'; - -interface VisitFormProps { - patientUuid: string; - closeWorkspace: () => void; -} - -const VisitForm: React.FC = ({ patientUuid, closeWorkspace }) => { - const { t } = useTranslation(); - const isTablet = useLayoutType() === 'tablet'; - const locations = useLocations(); - const sessionUser = useSession(); - const { defaultFacility, isLoading: loadingDefaultFacility } = useDefaultLoginLocation(); - - const config = useConfig(); - const [contentSwitcherIndex, setContentSwitcherIndex] = useState(config.showRecommendedVisitTypeTab ? 0 : 1); - const [isMissingVisitType, setIsMissingVisitType] = useState(false); - const [isSubmitting, setIsSubmitting] = useState(false); - const [timeFormat, setTimeFormat] = useState(new Date().getHours() >= 12 ? 'PM' : 'AM'); - const [visitDate, setVisitDate] = useState(new Date()); - const [visitTime, setVisitTime] = useState(dayjs(new Date()).format('hh:mm')); - const state = useMemo(() => ({ patientUuid }), [patientUuid]); - const [ignoreChanges, setIgnoreChanges] = useState(true); - const { activePatientEnrollment } = useActivePatientEnrollment(patientUuid); - const [enrollment, setEnrollment] = useState(activePatientEnrollment[0]); - const { mutateQueueEntries } = useMutateQueueEntries(); - const visitQueueNumberAttributeUuid = config.visitQueueNumberAttributeUuid; - const [selectedLocation, setSelectedLocation] = useState(''); - const [visitType, setVisitType] = useState(''); - const [{ service, priority, status, sortWeight, queueLocation }, setVisitFormFields] = useState({ - service: null, - priority: null, - status: null, - sortWeight: null, - queueLocation: null, - }); - - useEffect(() => { - if (locations?.length && sessionUser) { - setSelectedLocation(sessionUser?.sessionLocation?.uuid); - } else if (!loadingDefaultFacility && defaultFacility) { - setSelectedLocation(defaultFacility?.uuid); - } - }, [defaultFacility, loadingDefaultFacility, locations, sessionUser]); - - const handleSubmit = useCallback( - (event) => { - event.preventDefault(); - - if (!visitType) { - setIsMissingVisitType(true); - return; - } - - setIsSubmitting(true); - - const [hours, minutes] = convertTime12to24(visitTime, timeFormat); - - const payload: NewVisitPayload = { - patient: patientUuid, - startDatetime: toDateObjectStrict( - toOmrsIsoString( - new Date(dayjs(visitDate).year(), dayjs(visitDate).month(), dayjs(visitDate).date(), hours, minutes), - ), - ), - visitType: visitType, - location: selectedLocation, - attributes: [], - }; - - const abortController = new AbortController(); - - saveVisit(payload, abortController).then((response) => { - // add new queue entry if visit created successfully - postQueueEntry( - response.data.uuid, - service, - patientUuid, - priority, - status, - sortWeight, - queueLocation, - visitQueueNumberAttributeUuid, - ) - .then(() => { - showSnackbar({ - kind: 'success', - isLowContrast: true, - title: t('startAVisit', 'Start a visit'), - subtitle: t( - 'startVisitQueueSuccessfully', - 'Patient has been added to active visits list and queue.', - `${hours} : ${minutes}`, - ), - }); - closeWorkspace(); - setIsSubmitting(false); - mutateQueueEntries(); - }) - .catch((error) => { - showSnackbar({ - title: t('queueEntryError', 'Error adding patient to the queue'), - kind: 'error', - subtitle: error?.message, - }); - setIsSubmitting(false); - }) - .catch((error) => { - showSnackbar({ - title: t('startVisitError', 'Error starting visit'), - kind: 'error', - subtitle: error?.message, - }); - setIsSubmitting(false); - }); - }); - }, - [ - closeWorkspace, - mutateQueueEntries, - patientUuid, - priority, - queueLocation, - selectedLocation, - service, - sortWeight, - status, - t, - timeFormat, - visitDate, - visitQueueNumberAttributeUuid, - visitTime, - visitType, - ], - ); - - const handleOnChange = useCallback(() => { - setIgnoreChanges((prevState) => !prevState); - }, []); - - return ( -
-
- {isTablet && ( - - - - )} - -
-
{t('dateAndTimeOfVisit', 'Date and time of visit')}
-
- setVisitDate(date)} - value={visitDate}> - - - - setVisitTime(event.target.value as amPm)} - pattern="^(1[0-2]|0?[1-9]):([0-5]?[0-9])$" - style={{ marginLeft: '0.125rem', flex: 'none' }} - value={visitTime}> - setTimeFormat(event.target.value as amPm)} - value={timeFormat} - labelText={t('time', 'Time')} - aria-label={t('time', 'Time')}> - - - - - -
-
- -
-
{t('facility', 'Facility')}
- -
- - {config.showRecommendedVisitTypeTab && ( -
-
{t('program', 'Program')}
- - - setEnrollment(activePatientEnrollment.find(({ program }) => program.uuid === uuid)) - } - name="program-type-radio-group" - valueSelected="default-selected"> - {activePatientEnrollment.map(({ uuid, display, program }) => ( - - ))} - - -
- )} -
-
{t('visitType', 'Visit Type')}
- {config.showRecommendedVisitTypeTab && ( - setContentSwitcherIndex(index)}> - - - - )} - {config.showRecommendedVisitTypeTab && contentSwitcherIndex === 0 && ( - { - setVisitType(visitType); - setIsMissingVisitType(false); - }} - patientUuid={patientUuid} - patientProgram={enrollment} - locationUuid={selectedLocation} - /> - )} - {(!config.showRecommendedVisitTypeTab || contentSwitcherIndex === 1) && ( - { - setVisitType(visitType); - setIsMissingVisitType(false); - }} - /> - )} -
- {isMissingVisitType && ( -
- -
- )} - - -
-
- - - - -
- ); -}; - -export default VisitForm; diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.component.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.component.tsx deleted file mode 100644 index d5827f14e..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.component.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import React, { useState, useMemo, useEffect } from 'react'; -import classNames from 'classnames'; -import isEmpty from 'lodash-es/isEmpty'; -import { useTranslation } from 'react-i18next'; -import { - InlineNotification, - Layer, - RadioButton, - RadioButtonGroup, - Search, - StructuredListSkeleton, - Tile, -} from '@carbon/react'; -import { ResponsiveWrapper, useDebounce, useLayoutType, useVisitTypes, type VisitType } from '@openmrs/esm-framework'; -import { type PatientProgram } from '../../types'; -import { useRecommendedVisitTypes } from '../hooks/useRecommendedVisitTypes'; -import EmptyDataIllustration from '../empty-data-illustration.component'; -import styles from './visit-type-selector.scss'; - -export interface VisitTypeSelectorProps { - onChange: (event) => void; -} - -export const VisitTypeSelector: React.FC = ({ onChange }) => { - const allVisitTypes = useVisitTypes(); - - return ( -
- {allVisitTypes.length == 0 ? ( - - ) : ( - - )} -
- ); -}; - -export interface RecommendedVisitTypeSelectorProps { - onChange: (event) => void; - patientUuid: string; - patientProgram: PatientProgram; - locationUuid: string; -} - -/** Recommended visits are specfic to a patient, patient program, and location. */ -export const RecommendedVisitTypeSelector: React.FC = ({ - onChange, - patientUuid, - patientProgram, - locationUuid, -}) => { - const { t } = useTranslation(); - const { recommendedVisitTypes, error, isLoading } = useRecommendedVisitTypes( - patientUuid, - patientProgram?.uuid, - patientProgram?.program?.uuid, - locationUuid, - ); - - return ( -
- {isLoading ? ( - - ) : ( - - )} - {error && ( - - )} -
- ); -}; - -interface VisitTypeSelectorPresentationProps { - onChange: (event) => void; - visitTypes: VisitType[]; -} - -const MAX_RESULTS = 5; - -const VisitTypeSelectorPresentation: React.FC = ({ visitTypes, onChange }) => { - const { t } = useTranslation(); - const isTablet = useLayoutType() === 'tablet'; - const [searchTerm, setSearchTerm] = useState(''); - const debouncedSearchTerm = useDebounce(searchTerm); - const [selectedVisitType, setSelectedVisitType] = useState(''); - - const results = useMemo(() => { - if (!isEmpty(debouncedSearchTerm)) { - return visitTypes.filter( - (visitType) => visitType.display.toLowerCase().search(debouncedSearchTerm.toLowerCase()) !== -1, - ); - } else { - return visitTypes; - } - }, [debouncedSearchTerm, visitTypes]); - - const truncatedResults = results.slice(0, MAX_RESULTS); - - useEffect(() => { - if (results.length > 0) { - onChange(results[0].uuid); - setSelectedVisitType(results[0].uuid); - } - }, [onChange, results]); - - return ( -
- {truncatedResults.length < visitTypes.length ? ( - - setSearchTerm(event.target.value)} - placeholder={t('searchForAVisitType', 'Search for a visit type')} - labelText="" - /> - - ) : null} - {truncatedResults.length ? ( - { - setSelectedVisitType(visitType); - onChange(visitType); - }} - name="radio-button-group" - valueSelected={selectedVisitType}> - {truncatedResults.map(({ uuid, display, name }) => ( - - ))} - {/* TODO: This is supposed to paginate. Right now it just shows the user a truncated list - with no indication that the list is truncated. */} - - ) : ( - - - -

- {t('noVisitTypesMatchingSearch', 'There are no visit types matching this search text')} -

-
-
- )} -
- ); -}; diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.scss b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.scss deleted file mode 100644 index f93b62baf..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.scss +++ /dev/null @@ -1,100 +0,0 @@ -@use '@carbon/layout'; -@use '@carbon/type'; -@use '@openmrs/esm-styleguide/src/vars' as *; - -.visitTypeOverviewWrapper { - margin: layout.$spacing-05 0; - border: 0.0625rem solid $grey-2; -} - -.tablet { - background-color: $ui-02; -} - -.desktop { - background-color: $ui-01; - - .paginationContainer div { - background-color: $ui-01; - } -} - -.visitTypeOverviewWrapper div:nth-child(3) > div:nth-child(2) { - position: relative; -} - -.visitTypeOverviewWrapper div:nth-child(3) span * { - display: none; -} - -.radioButtonGroup { - display: flex; - flex-direction: column; - align-items: flex-start; - margin-top: layout.$spacing-03; - min-height: layout.$spacing-10; - width: 100%; - @include type.type-style('body-compact-01'); -} - -.radioButton { - padding: layout.$spacing-02 layout.$spacing-05; - margin: layout.$spacing-03 0; -} - -.content { - @include type.type-style('heading-compact-01'); - color: $text-02; - margin-top: layout.$spacing-05; - margin-bottom: layout.$spacing-03; -} - -.desktopHeading { - h4 { - @include type.type-style('heading-compact-02'); - color: $text-02; - } -} - -.tabletHeading { - h4 { - @include type.type-style('heading-03'); - color: $text-02; - } -} - -.desktopHeading, -.tabletHeading { - text-align: left; - text-transform: capitalize; - margin-bottom: layout.$spacing-05; - - h4:after { - content: ''; - display: block; - width: layout.$spacing-07; - padding-top: 0.188rem; - border-bottom: 0.375rem solid var(--brand-03); - } -} - -.heading:after { - content: ''; - display: block; - width: layout.$spacing-07; - padding-top: 0.188rem; - border-bottom: 0.375rem solid var(--brand-03); -} - -.tile { - text-align: center; - border: 1px solid $ui-03; -} - -// Overriding styles for RTL support -html[dir='rtl'] { - .desktopHeading, - .tabletHeading { - text-align: right; - } -} diff --git a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.test.tsx b/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.test.tsx deleted file mode 100644 index f94105bce..000000000 --- a/packages/esm-service-queues-app/src/patient-search/visit-form/visit-type-selector.test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable testing-library/no-node-access */ -import React from 'react'; -import userEvent from '@testing-library/user-event'; -import { render, screen } from '@testing-library/react'; -import { mockVisitTypes } from '__mocks__'; -import { useVisitTypes } from '@openmrs/esm-framework'; -import { VisitTypeSelector } from './visit-type-selector.component'; - -const mockUseVisitTypes = jest.mocked(useVisitTypes); - -describe('VisitTypeSelector', () => { - beforeEach(() => { - mockUseVisitTypes.mockReturnValue(mockVisitTypes); - }); - - it('renders visit types with no search bar if there are 5 or fewer', () => { - const fewVisitTypes = mockVisitTypes.slice(0, 3); - mockUseVisitTypes.mockReturnValue(fewVisitTypes); - render( {}} />); - - expect(screen.queryByRole('searchbox')).not.toBeInTheDocument(); - - fewVisitTypes.forEach((visitType) => { - const radioButton = screen.getByLabelText(visitType.display); - expect(radioButton).toBeInTheDocument(); - }); - }); - - it('renders the first 5 visit types with a search bar if there are more than 5', () => { - render( {}} />); - - expect(screen.getByRole('searchbox')).toBeInTheDocument(); - - mockVisitTypes.slice(0, 5).forEach((visitType) => { - const radioButton = screen.getByLabelText(visitType.display); - expect(radioButton).toBeInTheDocument(); - }); - }); - - it('filters by search input and calls onChange', async () => { - const user = userEvent.setup(); - - const mockOnChange = jest.fn(); - render(); - - const searchInput = screen.getByRole('searchbox').closest('input'); - await user.type(searchInput, 'hiv'); - - expect(searchInput.value).toBe('hiv'); - expect(screen.getByLabelText('HIV Return Visit')).toBeInTheDocument(); - expect(screen.getByLabelText('HIV Initial Visit')).toBeInTheDocument(); - expect(screen.queryByLabelText('Outpatient Visit')).not.toBeInTheDocument(); - expect(screen.queryByLabelText('Diabetes Clinic Visit')).not.toBeInTheDocument(); - - expect(mockOnChange).toHaveBeenLastCalledWith( - mockVisitTypes.filter((vt) => vt.display == 'HIV Return Visit')[0].uuid, - ); - }); - - it('calls onChange when a visit type is selected', async () => { - const user = userEvent.setup(); - - const mockOnChange = jest.fn(); - render(); - - const radioButton = screen.getByLabelText(mockVisitTypes[1].display).closest('input'); - await user.click(radioButton); - expect(radioButton).toBeChecked(); - expect(mockOnChange).toHaveBeenLastCalledWith(mockVisitTypes[1].uuid); - }); - - it('allows changing the search input if no results are returned from a search', async () => { - const user = userEvent.setup(); - - render( {}} />); - - const searchInput: HTMLInputElement = screen.getByRole('searchbox'); - await user.type(searchInput, 'asdfasdf'); - - const searchInputAfter: HTMLInputElement = screen.getByRole('searchbox'); - expect(searchInputAfter).toBeInTheDocument(); - expect(screen.getByText(/no visit types/i)).toBeInTheDocument(); - }); -}); diff --git a/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx index ba3d6b4b2..033a735ea 100644 --- a/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx +++ b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.test.tsx @@ -6,8 +6,8 @@ import QueueRoomForm from './queue-room-form.workspace'; const mockUseLayoutType = jest.mocked(useLayoutType); -jest.mock('../patient-search/hooks/useQueueLocations', () => ({ - ...jest.requireActual('../patient-search/hooks/useQueueLocations'), +jest.mock('../create-queue-entry/hooks/useQueueLocations', () => ({ + ...jest.requireActual('../create-queue-entry/hooks/useQueueLocations'), useQueueLocations: jest.fn(() => ({ queueLocations: { uuid: 'e7786d9a-ab62-11ec-b909-0242ac120002', display: 'Location Test' }, })), diff --git a/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.workspace.tsx b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.workspace.tsx index 378cb5831..f0a03994e 100644 --- a/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.workspace.tsx +++ b/packages/esm-service-queues-app/src/queue-rooms/queue-room-form.workspace.tsx @@ -14,7 +14,7 @@ import { } from '@carbon/react'; import { type DefaultWorkspaceProps, restBaseUrl, showSnackbar, useLayoutType } from '@openmrs/esm-framework'; import { mutate } from 'swr'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import { saveQueueRoom } from './queue-room.resource'; import styles from './queue-room-form.scss'; import { useQueues } from '../hooks/useQueues'; diff --git a/packages/esm-service-queues-app/src/queue-services/queue-service-form.test.tsx b/packages/esm-service-queues-app/src/queue-services/queue-service-form.test.tsx index d908398da..94b43689d 100644 --- a/packages/esm-service-queues-app/src/queue-services/queue-service-form.test.tsx +++ b/packages/esm-service-queues-app/src/queue-services/queue-service-form.test.tsx @@ -23,7 +23,7 @@ jest.mock('./queue-service.resource', () => ({ saveQueue: jest.fn(() => Promise.resolve({ status: 201 })), })); -jest.mock('../patient-search/hooks/useQueueLocations', () => ({ +jest.mock('../create-queue-entry/hooks/useQueueLocations', () => ({ useQueueLocations: () => ({ queueLocations: [ { id: '34567eb0-b035-4acd-b284-da45f5067502', name: 'Location 1' }, diff --git a/packages/esm-service-queues-app/src/queue-services/queue-service-form.workspace.tsx b/packages/esm-service-queues-app/src/queue-services/queue-service-form.workspace.tsx index 03323c1db..36e995bb3 100644 --- a/packages/esm-service-queues-app/src/queue-services/queue-service-form.workspace.tsx +++ b/packages/esm-service-queues-app/src/queue-services/queue-service-form.workspace.tsx @@ -15,7 +15,7 @@ import { import { mutate } from 'swr'; import { type DefaultWorkspaceProps, restBaseUrl, showSnackbar } from '@openmrs/esm-framework'; import { saveQueue, useServiceConcepts } from './queue-service.resource'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import styles from './queue-service-form.scss'; const QueueServiceForm: React.FC = ({ closeWorkspace }) => { diff --git a/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx b/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx index 8eb9adb09..969b838e3 100644 --- a/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx +++ b/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx @@ -27,7 +27,7 @@ import QueueTableExpandedRow from './queue-table-expanded-row.component'; import QueueTable from './queue-table.component'; import styles from './queue-table.scss'; -const serviceQueuesPatientSearchWorkspace = 'service-queues-patient-search'; +const serviceQueuesPatientSearchWorkspace = 'create-queue-entry-workspace'; /* Component with default values / sub-components passed into the more generic QueueTable. diff --git a/packages/esm-service-queues-app/src/queue-table/default-queue-table.test.tsx b/packages/esm-service-queues-app/src/queue-table/default-queue-table.test.tsx index 1eaa9d4c6..328105a8f 100644 --- a/packages/esm-service-queues-app/src/queue-table/default-queue-table.test.tsx +++ b/packages/esm-service-queues-app/src/queue-table/default-queue-table.test.tsx @@ -13,7 +13,7 @@ import { import { renderWithSwr } from 'tools'; import { useQueueEntries } from '../hooks/useQueueEntries'; import { useQueueRooms } from '../add-provider-queue-room/add-provider-queue-room.resource'; -import { useQueueLocations } from '../patient-search/hooks/useQueueLocations'; +import { useQueueLocations } from '../create-queue-entry/hooks/useQueueLocations'; import { type ConfigObject, configSchema } from '../config-schema'; import DefaultQueueTable from '../queue-table/default-queue-table.component'; @@ -29,8 +29,8 @@ jest.mock('../hooks/useQueues', () => { }; }); -jest.mock('../patient-search/hooks/useQueueLocations', () => ({ - ...jest.requireActual('../patient-search/hooks/useQueueLocations'), +jest.mock('../create-queue-entry/hooks/useQueueLocations', () => ({ + ...jest.requireActual('../create-queue-entry/hooks/useQueueLocations'), useQueueLocations: jest.fn(), })); diff --git a/packages/esm-service-queues-app/src/routes.json b/packages/esm-service-queues-app/src/routes.json index 030ee8100..2358464d3 100644 --- a/packages/esm-service-queues-app/src/routes.json +++ b/packages/esm-service-queues-app/src/routes.json @@ -66,7 +66,7 @@ { "name": "visit-form-queue-fields", "component": "visitFormQueueFields", - "slot":"visit-form-queue-slot" + "slot":"visit-form-bottom-slot" } ], "modals": [ @@ -127,9 +127,9 @@ "type": "service-queues" }, { - "name": "service-queues-patient-search", - "title": "searchPatient", - "component": "patientSearchWorkspace", + "name": "create-queue-entry-workspace", + "title": "addPatientToQueue", + "component": "createQueueEntryWorkspace", "type": "service-queues" } ] diff --git a/packages/esm-service-queues-app/src/types/index.ts b/packages/esm-service-queues-app/src/types/index.ts index f725066c0..7b36f6167 100644 --- a/packages/esm-service-queues-app/src/types/index.ts +++ b/packages/esm-service-queues-app/src/types/index.ts @@ -2,14 +2,6 @@ import { type Visit, type OpenmrsResource, type Location, type Patient } from '@ import type React from 'react'; import { type ColumnConfig } from '../config-schema'; -export enum SearchTypes { - BASIC = 'basic', - ADVANCED = 'advanced', - SEARCH_RESULTS = 'search_results', - SCHEDULED_VISITS = 'scheduled-visits', - VISIT_FORM = 'visit_form', -} - export interface Attribute { attributeType: OpenmrsResource; display: string; diff --git a/packages/esm-service-queues-app/src/views/queue-tables-for-all-statuses.component.tsx b/packages/esm-service-queues-app/src/views/queue-tables-for-all-statuses.component.tsx index 8f23f0385..3ef30ad7d 100644 --- a/packages/esm-service-queues-app/src/views/queue-tables-for-all-statuses.component.tsx +++ b/packages/esm-service-queues-app/src/views/queue-tables-for-all-statuses.component.tsx @@ -58,7 +58,7 @@ const QueueTablesForAllStatuses: React.FC = ({ size: isDesktop(layout) ? 'sm' : 'lg', }, selectPatientAction: (selectedPatientUuid) => { - launchWorkspace('service-queues-patient-search', { + launchWorkspace('create-queue-entry-workspace', { selectedPatientUuid, currentServiceQueueUuid: selectedQueue.uuid, }); diff --git a/packages/esm-service-queues-app/translations/en.json b/packages/esm-service-queues-app/translations/en.json index c611c8658..918d1b4ea 100644 --- a/packages/esm-service-queues-app/translations/en.json +++ b/packages/esm-service-queues-app/translations/en.json @@ -1,6 +1,7 @@ { "actions": "Actions", "addAProviderQueueRoom": "Add a provider queue room?", + "addedPatientToQueue": "Added patient to queue", "addNewQueueService": "Add New Queue Service", "addNewQueueServiceRoom": "Add new queue service room", "addNewService": "Add new service", @@ -17,10 +18,8 @@ "alistOfClients": "A list of clients waiting for ", "all": "All", "and": "And", - "anotherVisitType": "Start another visit type", "applyFilters": "Apply filters", "averageWaitTime": "Average wait time today", - "backToScheduledVisits": "Back To Scheduled Visits", "backToSearchResults": "Back to search results", "between": "Between", "bmi": "Bmi", @@ -45,7 +44,6 @@ "currentVisit": "Current visit", "date": "Date", "date&Time": "Date & time", - "dateAndTimeOfVisit": "Date and time of visit", "delete": "Delete", "deleteQueueEntry": "Delete queue entry", "discard": "Discard", @@ -63,18 +61,14 @@ "errorAddingQueue": "Error adding queue", "errorAddingQueueRoom": "Error adding queue room", "errorClearingQueues": "Error clearing queues", - "errorFetchingAppointments": "Error fetching appointments", + "errorFetchingVisit": "Error fetching patient visit", "errorLoadingQueueEntries": "Error loading queue entries", "errorPostingToScreen": "Error posting to screen", - "facility": "Facility", - "failedToLoadRecommendedVisitTypes": "Failed to load recommended visit types", "femaleLabelText": "Female", "filter": "Filter", "filterByService": "Filter by service:", "filterByStatus": "Filter by status:", "filterTable": "Filter table", - "futureScheduledVisits_one": "{{count}} visit scheduled for dates in the future", - "futureScheduledVisits_other": "{{count}} visits scheduled for dates in the future", "gender": "Gender", "heartRate": "Heart rate", "height": "Height", @@ -95,13 +89,11 @@ "missingQueueRoomName": "Missing queue room name", "missingQueueRoomService": "Missing queue room service", "missingService": "Missing service", - "missingVisitType": "Missing visit type", "modifyDefaultValue": "Modify default value", "movePatientToNextService": "Move patient to the next service?", "moveToNextService": "Move to next service", "name": "Name", "nextPage": "Next page", - "noAppointmentsFound": "No appointments found", "noColumnsDefined": "No table columns defined. Check Configuration", "noEncountersFound": "No encounters found", "noLastEncounter": "There is no last encounter to display for this patient", @@ -120,16 +112,12 @@ "noStatusConfigured": "No status configured", "notableConfig": "No table configuration", "notes": "Notes", - "noVisitTypesMatchingSearch": "There are no visit types matching this search text", "noVitalsFound": "No vitals found", "onTime": "On time", "orderDurationAndUnit": "for {{duration}} {{durationUnit}}", "orderIndefiniteDuration": "Indefinite duration", - "orInProperFormat": "Or", "patientAge": "Age", - "patientAlreadyInQueue": "Patient is already in the queue", "patientAttendingService": "Patient attending service", - "patientHasActiveVisit": "The patient already has an active visit", "patientInfo": "{{name}}{{sexInfo}}{{ageInfo}}", "patientList": "Patient list", "patientName": "Patient name", @@ -147,7 +135,6 @@ "priority": "Priority", "priorityComment": "Priority comment", "priorityIsRequired": "Priority is required", - "program": "Program", "provider": "Provider", "quantity": "Quantity", "queueAddedSuccessfully": "Queue added successfully", @@ -181,9 +168,6 @@ "queueScreen": "Queue screen", "queueService": "Queue service", "queueStatus": "Queue status", - "recentScheduledVisits_one": "{{count}} visit scheduled for +/- 7 days", - "recentScheduledVisits_other": "{{count}} visits scheduled for +/- 7 days", - "recommended": "Recommended", "refills": "Refills", "removeFromQueueAndEndVisit": "Remove patient from queue and end active visit", "removePatient": "Remove patient", @@ -197,22 +181,15 @@ "save": "Save", "scheduledAppointmentsList": "Scheduled appointments patient list", "scheduledToday": "Scheduled for today", - "search": "Search", - "searchForAVisitType": "Search for a visit type", - "searchPatient": "Search Patient", "searchThisList": "Search this list", "selectALocation": "Select a location", "selectAVisitType": "Select visit type", - "selectFacility": "Select a facility", - "selectOption": "Select an option", - "selectProgramType": "Select program type", "selectQueue": "Select a queue", "selectQueueLocation": "Select a queue location", "selectQueueService": "Select a queue service", "selectRoom": "Select a room", "selectService": "Select a service", "selectServiceType": "Select a service type", - "selectVisitType": "Please select a Visit Type", "serve": "Serve", "servePatient": "Serve patient", "service": "Service", @@ -222,10 +199,6 @@ "sortWeight": "Sort weight", "sp02": "Sp02", "startAgeRangeInvalid": "Start age range is not valid", - "startAVisit": "Start a visit", - "startVisit": "Start visit", - "startVisitError": "Error starting visit", - "startVisitQueueSuccessfully": "Patient has been added to active visits list and queue.", "status": "Status", "statusIsRequired": "Status is required", "submitting": "Submitting...",