import { Checkbox } from '../ui/checkbox'; import { Heading } from '../ui/heading'; import { DismissibleAlert } from '../ui/dismissible-alert'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../ui/alert-dialog'; import { CLINICIAN_SEAT_PERMITS, type ClinicianSeatCharge } from '../../utils/clinicianSeatCharge'; /** * THE CLINICIAN SEAT CONTROL, AND THE CONFIRMATION THAT PRECEDES THE CHARGE. * * Extracted from `StaffWorkspacesPage` when the staff CREATION form gained the same control. It is * shared rather than copied for one reason: **this control is the last thing somebody reads before a * recurring charge starts.** Two copies of that sentence drift, and the copy that goes stale is the one * a practice reads immediately before being billed - which is the defect this exists for, on * the screen that had only one copy. * * THE PREREQUISITE IS THE PRACTITIONER WORKSPACE, and the dependency runs in that direction. A seat on * somebody who is not a practitioner is what made a receptionist bookable for a consultation * so the box is disabled until Practitioner is ticked and the refusal names the remedy in the same * breath. * * A DISABLED CONTROL IS A HINT, NOT A GATE. Every caller must apply the same test at save time; this * component renders the decision, it never enforces it. */ export interface ClinicianSeatFieldProps { className?: string; /** Is the box ticked in the draft? */ checked: boolean; onCheckedChange: (checked: boolean) => void; /** Has the Practitioner workspace been selected? The seat is meaningless without it. */ practitionerWorkspaceSelected: boolean; /** * Already saved as a clinician. * * IT NO LONGER LOCKS THE BOX. Turning a seat off has to cancel or move that clinician's booked * patients first, and while no flow did that the off-switch was withheld and said to be withheld - * which left a practice that granted a seat by mistake billed for it indefinitely, with no screen * anywhere that could stop the charge. That flow exists now, so unticking is allowed and the CALLER * decides what happens next. * * Always false on a creation form, where nothing has been saved and there is nothing to remove. */ savedEnabled?: boolean; disabled?: boolean; headingLevel?: 3 | 6; } export const ClinicianSeatField = ({ className = 'mt-4 pt-4 border-t border-border', checked, onCheckedChange, practitionerWorkspaceSelected, savedEnabled = false, disabled = false, headingLevel = 6, }: ClinicianSeatFieldProps) => { // A SAVED seat is unlockable now: it is the only route a practice has to stop paying for one. The // Practitioner prerequisite still holds for GRANTING, which is the direction that made a // receptionist bookable for a consultation. const locked = disabled || (!savedEnabled && !practitionerWorkspaceSelected); return (
Clinician seat
onCheckedChange(next === true)} />
{/* THE OFF-SWITCH EXISTS NOW, and the sentence that used to sit here said it did not. Withholding it was defensible while nothing dealt with the booked patients; leaving it withheld meant a practice that granted a seat by mistake was billed for it forever with no screen that could stop the charge. Unticking opens a dialog that reads the clinician's calendar first and will not save until those patients have somewhere to go. */} {savedEnabled && ( Removing the seat makes them unbookable. Their booked patients have to be cancelled or moved first; the next screen does that and explains any change to your bill. )} {/* Says WHY the box is disabled rather than leaving a dead control. */} {!savedEnabled && !practitionerWorkspaceSelected && ( A clinician seat can only be given to somebody who has the Practitioner workspace. Select the Practitioner workspace first. )}
); }; /** * The money question, asked once, on the way ON. * * `open` is driven by the caller's pending-confirm state so the caller keeps control of what happens on * accept - a creation form submits, an edit form saves. The SENTENCE is what is shared, and it is the * whole point: it states the amount, or says plainly that we could not read it. Never an invented * figure immediately before a charge. */ export interface ClinicianSeatConfirmDialogProps { open: boolean; onOpenChange: (open: boolean) => void; seatCharge: ClinicianSeatCharge; onConfirm: () => void; } export const ClinicianSeatConfirmDialog = ({ open, onOpenChange, seatCharge, onConfirm, }: ClinicianSeatConfirmDialogProps) => ( Enable this clinician? {/* TWO FACTS, AND THE PERMISSION ONE COMES FIRST. An admin reading only about a subscription line is not being told who just became bookable. */} {CLINICIAN_SEAT_PERMITS} {' '} {seatCharge.headline} Don't enable {/* Three states, because there are three outcomes: nothing happens, a line joins a bill, or a card is asked for. "Enable and add to my bill" in front of a Stripe checkout says the money is settled when it is not. */} {seatCharge.startsSubscription ? 'Continue to payment' : seatCharge.chargeCents === 0 ? 'Enable clinician' : 'Enable and add to my bill'} ); /** * What somebody who may not add a clinician seat is told, where the seat control would have been. * * Spencer, 2026-09-16: when a person is not allowed to attach a paid clinician seat, say that only * administrators can add one and to contact them. Leaving the control out silently let a practitioner be * created without a seat and nobody know why. It says "paid" only when the seat is: the first clinician is * free, and this screen claims only what is true. */ export const ClinicianSeatWithheldNote = ({ practitionerWorkspaceSelected, free, seated = false, }: { practitionerWorkspaceSelected: boolean; free: boolean; /** The person already holds a seat, so the missing authority is the one that removes it. */ seated?: boolean; }) => seated ? ( Only administrators can remove a clinician seat. Please contact an administrator to remove it. ) : practitionerWorkspaceSelected ? ( {free ? 'Only administrators can add a clinician seat.' : 'Only administrators can add a new paid clinician seat.'} {' '}Please contact an administrator to add one. ) : null; export default ClinicianSeatField;