/** Sends a patient's request to correct a provider-authored record, as a message to its author. */ import { useCallback, useState } from 'react'; import type { Patient, Reference } from 'fhir/r4'; import { useCommunicationManager } from './useCommunicationManager'; import { useFhirPractitioners } from './useFhirPractitioners'; import { useAuthServiceContext, useCommunicationServiceContext, useFhirServiceContext } from '../services'; import { buildCorrectionRequestMessage } from '../utils/messaging/requestCorrection'; import { formatFhirHumanName } from '../utils/fhirHumanName'; import { namedSend } from '../utils/messaging/sendOperation'; import { MAY_HAVE_BEEN_SENT } from './useHeldSendOperations'; import { useScopedSendRecovery } from './useScopedSendRecovery'; import { describeServerRefusal } from '../utils/serverRefusalMessage'; import { isSaveRetired } from '../utils/saveOperation'; export const CANNOT_IDENTIFY_RECIPIENT_MESSAGE = "We can't tell who recorded this, so a request can't be sent automatically. Message your care team from Messages instead."; /** `shownAuthor` is the author of the record on screen, so an unconfirmed request to them is found again after a reload. */ export const useRequestRecordCorrection = (shownAuthor?: Reference) => { const { openDirectThread } = useCommunicationManager(); const { createCommunication, createCommunicationForThread, addCommunicationToState, messagingIdentity, } = useCommunicationServiceContext(); const { extractReferenceId, createReferenceObject } = useFhirServiceContext(); const { practitionersList } = useFhirPractitioners(); const { currentUser } = useAuthServiceContext(); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(); const shownId = shownAuthor ? extractReferenceId(shownAuthor) as string | undefined : undefined; const recovery = useScopedSendRecovery('record-correction', shownId ? `correction:${shownId}` : undefined); const requestCorrection = useCallback(async (author: Reference | undefined, recordLabel: string, note: string) => { setError(undefined); if (!note.trim()) return { ok: false as const }; const practitionerId = author ? (extractReferenceId(author) as string | undefined) : undefined; const withRoles = practitionerId ? practitionersList.find((item) => item.practitioner.id === practitionerId) : undefined; const activeRoles = (withRoles?.roles || []).filter((role) => role.active !== false); let organizationId: string | undefined; for (const role of activeRoles) { organizationId = extractReferenceId(role.organization) as string | undefined; if (organizationId) break; } const subjectPatientId = messagingIdentity?.id; if (!practitionerId || !organizationId || !subjectPatientId) { setError(CANNOT_IDENTIFY_RECIPIENT_MESSAGE); return { ok: false as const }; } const attempt = recovery.begin(`correction:${practitionerId}`); setSubmitting(true); const body = buildCorrectionRequestMessage(recordLabel, note); const recipient = formatFhirHumanName(withRoles?.practitioner?.name); const result = await recovery.run(attempt, `correction:${practitionerId}|${body}`, async (operation, leaving) => { const thread = await openDirectThread(subjectPatientId, practitionerId, 'practitioner', organizationId); attempt.admit(); if (!thread.threadId) throw new Error('Could not send the correction request. Try again.'); const recipientRef = createReferenceObject('Practitioner', practitionerId, recipient); const message = createCommunicationForThread( body, thread.threadId as string, currentUser?.fhir as Patient, [recipientRef], ); leaving(); return createCommunication(namedSend(message, operation), operation); }); setSubmitting(false); // The thread was opened for a patient or practice no longer on screen: nothing is written for someone else's screen. if (result.state === 'retired') return { ok: false as const }; if (result.state === 'sent') { addCommunicationToState(result.value); return { ok: true as const, practitionerId, recipient: recipient || undefined }; } if (result.state === 'not-sent') { setError(result.reason); return { ok: false as const }; } if (isSaveRetired(result.error)) return { ok: false as const }; console.error('Error requesting a correction:', result.error); setError(result.unsure ? 'Your correction request was not confirmed. ' + MAY_HAVE_BEEN_SENT : describeServerRefusal(result.error, 'Your correction request was not sent')); return { ok: false as const }; }, [ openDirectThread, createCommunication, createCommunicationForThread, addCommunicationToState, messagingIdentity, extractReferenceId, createReferenceObject, practitionersList, currentUser, recovery, ]); return { requestCorrection, submitting, error, recovery }; };