import React, { useMemo } from 'react'; import { useSWRConfig } from 'swr'; import { OverflowMenu, OverflowMenuItem, SkeletonText, Tag, Tile } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { ExtensionSlot, formatDatetime, launchWorkspace2, parseDate, type Session, showModal, showSnackbar, useConfig, userHasAccess, useSession, } from '@openmrs/esm-framework'; import { updateMedicationRequestFulfillerStatus, usePrescriptionDetails, } from '../medication-request/medication-request.resource'; import { deleteMedicationDispense } from '../medication-dispense/medication-dispense.resource'; import { type MedicationDispense, MedicationDispenseStatus, type MedicationRequestBundle } from '../types'; import { PRIVILEGE_DELETE_DISPENSE, PRIVILEGE_DELETE_DISPENSE_THIS_PROVIDER_ONLY, MEDICATION_DISPENSE_ACTION_SLOT, PRIVILEGE_EDIT_DISPENSE, } from '../constants'; import { computeNewFulfillerStatusAfterDelete, computeQuantityRemaining, computeTotalQuantityDispensed, getFulfillerStatus, getMedicationRequestBundleContainingMedicationDispense, getUuidFromReference, markEncounterAsStale, revalidate, sortMedicationDispensesByWhenHandedOver, } from '../utils'; import { type PharmacyConfig } from '../config-schema'; import MedicationEvent from '../components/medication-event.component'; import styles from './history-and-comments.scss'; const HistoryAndComments: React.FC<{ encounterUuid: string; patientUuid: string; }> = ({ encounterUuid, patientUuid }) => { const { t } = useTranslation(); const config = useConfig(); const { medicationRequestBundles, prescriptionDate, error, isLoading } = usePrescriptionDetails( encounterUuid, config.refreshInterval, ); const generateDispenseVerbiage = (medicationDispense: MedicationDispense): string | null => { if (medicationDispense.status === MedicationDispenseStatus.completed) { return t('dispensedMedication', 'dispensed medication'); } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) { return t('pausedDispense', 'paused dispense'); } else if (medicationDispense.status === MedicationDispenseStatus.declined) { return t('closedDispense', 'closed dispense'); } else { return null; } }; const { sortedDispenses, requests, hasHistory } = useMemo(() => { const dispenses = medicationRequestBundles?.flatMap((bundle) => bundle.dispenses) ?? []; const requests = medicationRequestBundles?.flatMap((bundle) => bundle.request) ?? []; return { sortedDispenses: [...dispenses].sort(sortMedicationDispensesByWhenHandedOver), requests, hasHistory: dispenses.length > 0 || requests.length > 0, }; }, [medicationRequestBundles]); return (
{isLoading && ( )} {error && (

{t('errorLoadingHistory', 'Error loading history')}: {error.message}

)} {!isLoading && !error && !hasHistory && (

{t('noHistoryFound', 'No history found')}

)} {!isLoading && !error && hasHistory && ( <> {sortedDispenses.map((dispense) => (
{dispense.performer && dispense.performer[0]?.actor?.display} {generateDispenseVerbiage(dispense)} —{' '} {formatDatetime(parseDate(dispense.whenHandedOver))}
} isDispenseEvent>
))} {requests.map((request) => (
{request.requester.display} {t('orderedMedication', 'ordered medication')} —{' '} {formatDatetime(prescriptionDate)}
{t('ordered', 'Ordered')}} />
))} )}
); }; interface MedicationDispenseActionMenuProps { medicationDispense: MedicationDispense; medicationRequestBundle: MedicationRequestBundle; patientUuid: string; encounterUuid: string; } const MedicationDispenseActionMenu: React.FC = ({ medicationDispense, medicationRequestBundle, patientUuid, encounterUuid, }) => { const { t } = useTranslation(); const { mutate } = useSWRConfig(); const session = useSession(); const config = useConfig(); const userCanEdit = (session: Session): boolean => session?.user && userHasAccess(PRIVILEGE_EDIT_DISPENSE, session.user); const userCanDelete = (session: Session, medicationDispense: MedicationDispense): boolean => { if (session?.user) { if (userHasAccess(PRIVILEGE_DELETE_DISPENSE, session.user)) { return true; } else if ( userHasAccess(PRIVILEGE_DELETE_DISPENSE_THIS_PROVIDER_ONLY, session.user) && session.currentProvider?.uuid && medicationDispense.performer?.find( (performer) => performer?.actor?.reference?.length > 1 && performer.actor.reference.split('/')[1] === session.currentProvider.uuid, ) !== undefined ) { return true; } } return false; }; const getDispenseWorkspaceConfig = ( medicationDispense: MedicationDispense, medicationRequestBundle: MedicationRequestBundle, ): { workspaceName: string; props: Record } | undefined => { if (medicationDispense.status === MedicationDispenseStatus.completed) { // note that since this is an edit, quantity remaining needs to include quantity that is part of this dispense let quantityRemaining = null; let quantityDispensed = null; if (config.dispenseBehavior.restrictTotalQuantityDispensed) { quantityRemaining = computeQuantityRemaining(medicationRequestBundle) + (medicationDispense?.quantity ? medicationDispense.quantity.value : 0); if (medicationRequestBundle.dispenses) { quantityDispensed = computeTotalQuantityDispensed(medicationRequestBundle.dispenses); } } const dispenseFormProps = { patientUuid, encounterUuid, medicationDispense, medicationRequestBundle, quantityRemaining, quantityDispensed, mode: 'edit', }; return { workspaceName: 'dispense-workspace', props: dispenseFormProps }; } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) { const pauseDispenseFormProps = { patientUuid, encounterUuid, medicationDispense, mode: 'edit', }; return { workspaceName: 'pause-dispense-workspace', props: pauseDispenseFormProps }; } else if (medicationDispense.status === MedicationDispenseStatus.declined) { const closeDispenseFormProps = { patientUuid, encounterUuid, medicationDispense, mode: 'edit', }; return { workspaceName: 'close-dispense-workspace', props: closeDispenseFormProps }; } }; const getWorkspaceTitle = (medicationDispense: MedicationDispense): string | undefined => { if (medicationDispense.status === MedicationDispenseStatus.completed) { return t('editDispenseRecord', 'Edit Dispense Record'); } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) { return t('editPauseRecord', 'Edit Pause Record'); } else if (medicationDispense.status === MedicationDispenseStatus.declined) { return t('editCloseRecord', 'Edit Close Record'); } }; const handleDelete = ( medicationDispense: MedicationDispense, medicationRequestBundle: MedicationRequestBundle, ): void => { const currentFulfillerStatus = getFulfillerStatus(medicationRequestBundle.request); const newFulfillerStatus = computeNewFulfillerStatusAfterDelete( medicationDispense, medicationRequestBundle, config.dispenseBehavior.restrictTotalQuantityDispensed, ); markEncounterAsStale(encounterUuid); deleteMedicationDispense(medicationDispense.id) .then(() => { showSnackbar({ kind: 'success', title: t('success', 'Success'), subtitle: t('medicationDispenseDeleted', 'Medication dispense was deleted successfully'), }); if (currentFulfillerStatus !== newFulfillerStatus) { updateMedicationRequestFulfillerStatus( getUuidFromReference( medicationDispense.authorizingPrescription[0].reference, // assumes authorizing prescription exist ), newFulfillerStatus, ) .then(() => { revalidate(mutate, encounterUuid); }) .catch(() => { showSnackbar({ kind: 'error', title: t('updateStatusFailed', 'Update Status Failed'), subtitle: t('couldNotUpdateMedicationRequestStatus', 'Could not update medication request status'), }); }); } revalidate(mutate, encounterUuid); }) .catch(() => { showSnackbar({ kind: 'error', title: t('deleteFailed', 'Delete Failed'), subtitle: t('couldNotDeleteMedicationDispense', 'Could not delete medication dispense'), }); }); }; const editable = userCanEdit(session); const deletable = userCanDelete(session, medicationDispense); const handleEdit = () => { const { workspaceName, props } = getDispenseWorkspaceConfig(medicationDispense, medicationRequestBundle) as { workspaceName: string; props: Record; }; const customWorkspaceTitle = getWorkspaceTitle(medicationDispense); launchWorkspace2(workspaceName, { customWorkspaceTitle, ...props }); }; const handleDeleteClick = ({ medicationDispense, medicationRequestBundle }) => { const dispose = showModal('delete-confirm-modal', { title: t('deleteDispenseRecord', 'Delete Dispense Record'), message: t('deleteDispenseRecordMessage', 'Are you sure you want to delete this dispense record?'), onDelete: () => { handleDelete(medicationDispense, medicationRequestBundle); dispose(); }, onClose: () => { dispose(); }, }); }; if (!editable && !deletable) { return null; } else { return ( {editable && ( )} {deletable && ( handleDeleteClick({ medicationDispense, medicationRequestBundle })} /> )} ); } }; const DispenseTag: React.FC<{ medicationDispense: MedicationDispense }> = ({ medicationDispense }) => { const { t } = useTranslation(); if (medicationDispense.status === MedicationDispenseStatus.completed) { return {t('dispensed', 'Dispensed')}; } else if (medicationDispense.status === MedicationDispenseStatus.on_hold) { return {t('paused', 'Paused')}; } else if (medicationDispense.status === MedicationDispenseStatus.declined) { return {t('closed', 'Closed')}; } else { return null; } }; export default HistoryAndComments;