diff --git a/packages/esm-patient-search-app/src/index.ts b/packages/esm-patient-search-app/src/index.ts index 0ec66345b..f02745e9f 100644 --- a/packages/esm-patient-search-app/src/index.ts +++ b/packages/esm-patient-search-app/src/index.ts @@ -36,6 +36,11 @@ export const patientSearchWorkspace = getAsyncLifecycle( options, ); +export const startVisitConfirmationModal = getAsyncLifecycle( + () => import('./patient-search-page/patient-banner/banner/start-visit-confirmation.modal'), + options, +); + export function startupApp() { defineConfigSchema(moduleName, configSchema); diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx index 0e9ad924a..01aba0167 100644 --- a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/patient-banner.component.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState, useMemo } from 'react'; import classNames from 'classnames'; -import { SkeletonIcon, SkeletonText } from '@carbon/react'; +import { Button, SkeletonIcon, SkeletonText } from '@carbon/react'; import { ConfigurableLink, ExtensionSlot, @@ -9,6 +9,7 @@ import { PatientBannerToggleContactDetailsButton, PatientBannerPatientInfo, PatientPhoto, + showModal, useConfig, useLayoutType, useVisit, @@ -46,6 +47,24 @@ const PatientBanner: React.FC = ({ patient, patientUuid, hid setShowContactDetails((value) => !value); }, []); + const handleAddToQueueClick = () => { + if (!currentVisit && !isDeceased) { + handleLaunchStartVisitConfirmationModal(); + } else { + nonNavigationSelectPatientAction(patientUuid); + } + }; + + const handleLaunchStartVisitConfirmationModal = () => { + const dispose = showModal('start-visit-confirmation-modal', { + closeModal: () => { + dispose(); + }, + patientName, + patientUuid, + }); + }; + const fhirMappedPatient: fhir.Patient = useMemo(() => mapToFhirPatient(patient), [patient]); return ( @@ -80,17 +99,7 @@ const PatientBanner: React.FC = ({ patient, patientUuid, hid patientUuid={patientUuid} /> ) : null} - {!isDeceased && !currentVisit && ( - - )} + {!isDeceased && }
diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.modal.tsx b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.modal.tsx new file mode 100644 index 000000000..b96dad253 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.modal.tsx @@ -0,0 +1,69 @@ +import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, ModalHeader, ModalBody, ModalFooter } from '@carbon/react'; +import { launchWorkspace, showSnackbar } from '@openmrs/esm-framework'; +import styles from './start-visit-confirmation.scss'; + +interface StartVisitConfirmationModalProps { + closeModal: () => void; + patientName: string; + patientUuid: string; +} + +const StartVisitConfirmationModal: React.FC = ({ + closeModal, + patientName, + patientUuid, +}) => { + const { t } = useTranslation(); + + const handleStartVisit = useCallback(() => { + try { + launchWorkspace('start-visit-workspace-form', { + patientUuid: patientUuid, + showPatientHeader: true, + openedFrom: 'service-queues-add-patient', + }); + } catch (error) { + console.error('Error launching visit form workspace:', error); + + showSnackbar({ + isLowContrast: false, + kind: 'error', + title: t('errorStartingVisit', 'Error starting visit'), + subtitle: error.message ?? t('errorStartingVisitDescription', 'An error occurred while starting the visit'), + }); + } finally { + closeModal(); + } + }, [patientUuid, t, closeModal]); + + return ( + <> + + +

+ {t( + 'startVisitPrompt', + 'You must start a visit for {{name}} before adding them to the Active visits list. Do you want to start a visit now?', + { name: patientName }, + )} +

+
+ + + + + + ); +}; + +export default StartVisitConfirmationModal; diff --git a/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.scss b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.scss new file mode 100644 index 000000000..86d6877c7 --- /dev/null +++ b/packages/esm-patient-search-app/src/patient-search-page/patient-banner/banner/start-visit-confirmation.scss @@ -0,0 +1,30 @@ +@use '@carbon/layout'; +@use '@openmrs/esm-styleguide/src/vars' as *; + +.modalHeader { + :global { + .cds--modal-close-button { + position: absolute; + inset-block-start: 0; + inset-inline-end: 0; + margin: 0; + margin-top: calc(-1 * #{layout.$spacing-05}); + } + + .cds--modal-close { + background-color: rgba(0, 0, 0, 0); + + &:hover { + background-color: var(--cds-layer-hover); + } + } + + .cds--popover--left > .cds--popover > .cds--popover-content { + transform: translate(-4rem, 0.85rem); + } + + .cds--popover--left > .cds--popover > .cds--popover-caret { + transform: translate(-3.75rem, 1.25rem); + } + } +} diff --git a/packages/esm-patient-search-app/src/routes.json b/packages/esm-patient-search-app/src/routes.json index b71b3469c..138245928 100644 --- a/packages/esm-patient-search-app/src/routes.json +++ b/packages/esm-patient-search-app/src/routes.json @@ -29,6 +29,12 @@ "offline": true } ], + "modals": [ + { + "name": "start-visit-confirmation-modal", + "component": "startVisitConfirmationModal" + } + ], "workspaces": [ { "component": "patientSearchWorkspace", diff --git a/packages/esm-patient-search-app/translations/en.json b/packages/esm-patient-search-app/translations/en.json index cce455516..8c12e5e66 100644 --- a/packages/esm-patient-search-app/translations/en.json +++ b/packages/esm-patient-search-app/translations/en.json @@ -2,6 +2,7 @@ "age": "Age", "any": "Any", "apply": "Apply", + "cancel": "Cancel", "clear": "Clear", "clearSearch": "Clear", "closeSearch": "Close Search Panel", @@ -15,6 +16,8 @@ "errorLoadingAttribute": "Error loading attribute type {{attributeUuid}}", "errorLoadingConceptAttributeAnswers": "Error loading concept attribute answers", "errorLoadingLocationsForAttribute": "Error loading locations for person attribute {{attributeName}}", + "errorStartingVisit": "Error starting visit", + "errorStartingVisitDescription": "An error occurred while starting the visit", "errorUpdatingRecentlyViewedPatients": "Error updating recently viewed patients", "female": "Female", "filtersAppliedText": "search queries added", @@ -42,6 +45,9 @@ "searchResultsCount_other": "{{count}} search results", "selectOption": "Select an option", "sex": "Sex", + "startVisit": "Start visit", + "startVisitPrompt": "You must start a visit for {{name}} before adding them to the Active visits list. Do you want to start a visit now?", + "startVisitQuestion": "Start visit?", "trySearchWithPatientUniqueID": "Try to search again using the patient's unique ID number", "unknown": "Unknown", "unsupportedAttributeFormat": "Unsupported attribute format: {{format}}", diff --git a/packages/esm-patient-search-app/translations/id.json b/packages/esm-patient-search-app/translations/id.json index cce455516..bd2d40d37 100644 --- a/packages/esm-patient-search-app/translations/id.json +++ b/packages/esm-patient-search-app/translations/id.json @@ -42,6 +42,9 @@ "searchResultsCount_other": "{{count}} search results", "selectOption": "Select an option", "sex": "Sex", + "startVisit": "Start visit", + "startVisitPrompt": "You must start a visit for {{name}} before adding them to the Active visits list. Do you want to start a visit now?", + "startVisitQuestion": "Start visit?", "trySearchWithPatientUniqueID": "Try to search again using the patient's unique ID number", "unknown": "Unknown", "unsupportedAttributeFormat": "Unsupported attribute format: {{format}}",