Callout Modal
Component hierarchy, interfaces, draft restore flow, API payload structure, and validation rules for the Critical Callout modal.
Callout Modal
The callout modal is the primary action surface. It is opened by CriticalNotificationButton from any entry point and renders inside CriticalNotificationModal, which splits the view into an action panel (left) and a history panel (right).
Component hierarchy
Key files:
| File | Purpose |
|---|---|
CriticalNotificationModal.tsx | Container โ fetches data, owns tab layout |
CriticalCalloutModal.tsx | Action panel โ all form sections + submit |
CriticalNotificationBody.tsx | Tab body switcher |
CriticalCalloutLogs.tsx | History timeline |
CriticalNotificationButton.tsx | Trigger button (reused on all surfaces) |
interfaces.ts | All TypeScript interfaces |
utils.ts | Restore state, validate, structure logs, flag tag |
helpers.tsx | API call helpers |
CriticalCalloutModal props
interface Props {
orgId: number;
isLoading: boolean;
billData: JsonObject;
onCloseModal: Function;
userDetails: JsonObject;
attemptedCallout?: AttemptedCallout; // present if a draft exists
setIsLoader: (isLoader: boolean) => void;
criticalReports: CriticalReportParameter[]; // reports to act on
onSuccess?: (reportIds: number[], status: number) => void;
}Core interfaces
File: interfaces.ts
AttemptedCallout โ draft restore payload from API
interface AlreadyNotified {
phone_call: { name: string; contact: string | number };
fax: { name: string; fax_number: string };
}
interface CriticalCalloutMeta {
communication_methods: string[]; // ["email", "fax", "sms", "whatsapp"]
notify_to_others: { name: string; email: string; fax: string; contactNumber: string };
log_message: string;
already_notified: AlreadyNotified; // phone/fax already-notified person details
callout_for?: number[]; // testIDs included in draft
}
interface AttemptedCallout {
recipients: string[];
critical_callout_comment: string;
critical_values_meta: CriticalValuesMeta[];
critical_callout_meta: CriticalCalloutMeta;
}RestoredCalloutState โ hydrated UI state
interface RestoredCalloutState {
comments: string;
alreadyInformed: boolean;
selectedMethod: number[]; // COMMUNICATION_METHOD ids
selectedReports: number[]; // labReportIds
selectedRecipients: number[]; // ACCOUNT | PROVIDER | PATIENT
informedVia: InformedViaState;
notifyOthers: CriticalCalloutNotifyOther;
}CriticalCalloutHistoryLogs โ history panel entry
interface CriticalCalloutHistoryLogs {
meta: {
is_draft?: boolean; // true = Attempted ยท false = Completed
callout_for?: string;
callout_forFormatted?: string; // computed by structureCalloutLogs()
method?: string;
methodFormatted?: string; // includes "Notified via Phone Call/Fax" if notify_other present
recipients?: string;
recipientsFormatted?: string; // includes already-informed contact details
callout_done_by?: string;
comment: string;
bill_id?: number[];
lab_report_ids?: number[];
organization?: { fax: string; name: string; email: string };
referral?: { fax: string; name: string; email: string };
patient?: { name: string; email: string; contact: string };
other?: { fax: string; name: string; email: string; contact: string };
notify_other?: InformedViaState; // already-notified person: phone + fax
};
labUserName: string;
activityText: string;
activityDate: string;
}Draft restore flow
File: utils.ts โ getRestoredCalloutState()
When attemptedCallout is present (a draft exists for the bill), a useEffect hydrates the modal state before the user interacts.
Communication channels and submit payload
File: utils.ts โ prepareCalloutPayload() / constants.ts โ COMMUNICATION_METHODS
Four channels: Email, Fax, SMS, WhatsApp.
// notification object built by prepareCalloutPayload()
{
is_patient, is_referral, is_organization,
notified_by_fax, notified_by_call,
comment,
other: { enable, name, email, faxNumber, contactNumber },
sendEmail: selectedCommunicationMethod.includes(EMAIL),
sendFax: selectedCommunicationMethod.includes(FAX),
sendSms: selectedCommunicationMethod.includes(SMS),
sendWhatsApp: selectedCommunicationMethod.includes(WHATSAPP),
}Final API payload:
{
is_draft: boolean, // true = Save Draft, false = Notify Done
bill_id: number,
lab_report_ids: number[],
notification: NotifyPayload,
}Validation rules
File: utils.ts
Validation runs before every submit. Save Draft only requires at least one report to be selected โ all other rules apply to Notify and Mark as Done.
Four dedicated validator functions, each returning FailedValidation { isValid, errorMessage }:
validateNotifyOther(notifyOthers, selectedCommunicationMethod)
Runs when the Notify Others section is enabled.
| Condition | Error |
|---|---|
name is empty | "Please enter the name for 'Notify Other'." |
| Email method selected + invalid/empty email | "Please enter a valid Email Id for 'Notify Other'." |
| SMS or WhatsApp method selected + invalid/empty contact | "Please enter a valid Contact Number for 'Notify Other'." |
validateAlreadyInformed(alreadyInformed, informedVia)
Runs when the Already Informed? toggle is on.
| Condition | Error |
|---|---|
| Neither phone nor fax enabled | "Please enter either Fax or Phone details for 'Already Informed'." |
| Phone enabled + name empty or invalid number | "Please enter Name and valid Contact Number for 'Notified via Phone Call'." |
| Fax enabled + name empty or invalid fax | "Please enter Name and valid Fax Number for 'Notified via Fax'." |
validateFax(criticalReports, selectedReports, selectedRecipients, orgDetails, referralData)
Runs when Fax is the selected communication method.
| Condition | Error |
|---|---|
| Any selected report is unsigned | "Cannot send Fax as few reports are not signed" |
| Account selected + no fax number | "Account do not have a Fax Number" |
| Account selected + invalid fax number | "Invalid Account Fax Number" |
| Provider selected + no fax number | "Provider do not have a Fax Number" |
| Provider selected + invalid fax number | "Invalid Provider Fax Number" |
validateEmail(selectedRecipients, orgDetails, referralData, userDetails)
Runs when Email is the selected communication method.
| Condition | Error |
|---|---|
| Account selected + no email | "Account do not have an Email Id" |
| Account selected + invalid email | "Invalid Account Email Id" |
| Provider selected + no email | "Provider do not have an Email Id" |
| Provider selected + invalid email | "Invalid Provider Email Id" |
| Patient selected + no email | "Patient do not have an Email Id" |
| Patient selected + invalid email | "Invalid Patient Email Id" |
Summary โ what blocks each action
| Rule | Draft | Done |
|---|---|---|
| At least one report selected | โ | โ |
| At least one communication method | โ | โ |
| Fax validation (unsigned reports, missing fax numbers) | โ | โ |
| Email validation (missing email addresses) | โ | โ |
| Notify Others validation (name, email, contact) | โ | โ |
| Already Informed validation (fax/phone details) | โ | โ |
mandatory_comments on + empty comment | โ | โ |
Critical value flag
File: utils.ts โ getCriticalValueTag()
Used to label each parameter in the report accordion.
| Input | Output |
|---|---|
value < lowerBound | "Critical Low" |
value > upperBound | "Critical High" |
| Non-numeric value | "Critical" |
History panel (CriticalCalloutLogs)
File: CriticalCalloutLogs.tsx ยท Processor: utils.ts โ structureCalloutLogs()
Renders a VerticalTimeline on the right side of the modal. Entries are sorted most-recent first. structureCalloutLogs() handles both old (regex-parsed activityText) and new (meta.*) log formats for backward compatibility.
meta.is_draft | Icon | Entry title |
|---|---|---|
false / absent | faCheckCircle green | Callout Completed |
true | faCircleXmark yellow | Callout Attempted |
Each entry displays: timestamp, performed by, callout for (test names), method, recipient(s), comment.
Already Notified person details in history
When a callout included an Already Informed entry (someone notified directly via phone or fax, outside digital channels), the history log reflects it by enriching the methodFormatted and recipientsFormatted computed fields:
meta.notify_other = {
fax: { enabled, name, number } โ "Notified via Fax"
phone: { enabled, name, number } โ "Notified via Phone Call"
}notify_other field present | Appended to methodFormatted | Appended to recipientsFormatted |
|---|---|---|
fax (non-empty) | "Notified via Fax" | "{name} ({number})" |
phone (non-empty) | "Notified via Phone Call" | "{name} ({number})" |
This means the history timeline shows the full picture of who was informed โ digital recipients (org/referral/patient/other) alongside physically-notified contacts โ all in one entry.