import type { CampaignCapabilities } from '../../../services/CampaignService'; // WHETHER THIS PRACTICE CAN SEND A CAMPAIGN BY TEXT, AND IF NOT, WHICH THING IS MISSING. // // The composer offered "Also send as a text message" as an ordinary enabled control at a practice with // SMS off and no carrier registration, opened a text editor for it, and quoted a per-recipient price. // A quoted price reads as a statement that the send is available and this is what it costs, so the // practice writes the message and meets the refusal - wherever it lives - after the work. // // THE REASON IS NAMED, NOT JUST THE CONTROL DISABLED. Not on the plan and registration not finished are // different states with different remedies, and a greyed control with no sentence sends an administrator // hunting for a setting they may already have switched on. A refusal names what is refused; that rule // does not stop applying because the refusal is a checkbox. // // THREE STATES, AND THE THIRD IS THE ONE THIS CODEBASE KEEPS PAYING FOR. `undefined` is NOT KNOWN - a // read that failed, or a server older than this client - and it must never coalesce to "unavailable". // Telling a practice it cannot send on the strength of a fact nobody measured is the same false // assertion as telling a patient they have no appointments when the request was refused. export type SmsChannelAvailability = | { available: true } | { available: false; reason: string; remedy: string } | { available: 'unknown' }; export interface SmsChannelInputs { capabilities?: CampaignCapabilities; /** The entitlement the server derives. `undefined` when it could not be read. */ smsPromotionsEnabled: boolean | undefined; /** How many SMS add-ons the practice holds. `undefined` when the add-ons could not be read. */ smsPromotionsHeld: number | undefined; /** Brand registration with the carriers. `undefined` when the server has not said. */ smsRegistrationApproved?: boolean; } /** * WHY THE ADD-ON COUNT SEPARATES THE TWO CASES. The entitlement is false both when the practice has not * bought SMS and when it has bought it and carrier registration has not completed - promotional SMS * ships dark until then, which is correct and is not what this is about. The add-on count is what tells * them apart: nothing held means it was never bought; something held with the entitlement still false * means the purchase landed and the registration has not. */ export const smsChannelAvailability = ( { smsPromotionsEnabled, smsPromotionsHeld, smsRegistrationApproved, capabilities }: SmsChannelInputs, ): SmsChannelAvailability => { if (!capabilities || capabilities.protocolVersion !== 2) return { available: 'unknown' }; if (!capabilities.implementedChannels.includes('sms')) return { available: false, reason: 'Text campaign execution is unavailable.', remedy: 'Keep the saved text and check again later.' }; const readiness = capabilities.readiness.sms; if (!readiness || readiness.state === 'unknown') return { available: 'unknown' }; if (readiness.state === 'blocked' && readiness.reason !== 'quiet-hours') return { available: false, reason: readiness.reason ?? 'Text campaign execution is not ready.', remedy: 'Resolve the readiness requirement before sending.' }; if (smsPromotionsEnabled === undefined) return { available: 'unknown' }; if (smsRegistrationApproved === false && (smsPromotionsHeld ?? 0) > 0) { return { available: false, reason: 'Text messages are on this practice’s plan, but carrier registration is not finished.', remedy: 'Submit the text message registration on the Organization page, then wait for the carriers.', }; } if (smsPromotionsEnabled) return { available: true }; if (smsPromotionsHeld === undefined) { return { available: false, reason: 'This practice cannot send text messages yet.', remedy: 'Check SMS messaging on the Subscription page.', }; } return smsPromotionsHeld > 0 ? { available: false, reason: 'Text messages are on this practice’s plan, but carrier registration is not finished.', remedy: 'Texts start sending once the carriers approve the practice. Nothing else is needed here.', } : { available: false, reason: 'SMS messaging is not on this practice’s plan, so a text campaign cannot be sent.', remedy: 'Add SMS messaging on the Subscription page. Email still sends without it.', }; };