import React from 'react'; import { Link } from 'react-router-dom'; import { ArrowRight, Check } from 'lucide-react'; import { formatMoney } from '../../utils/money'; import type { SubscriptionRow, SubscriptionRowKey } from './subscriptionRows'; // One row of the settled subscription page ( §4). // // EVERY ROW SHOWS WHAT YOU HAVE **AND** IS THE CONTROL FOR CHANGING IT. There is no second screen and // no mode switch - that is decision 1, and it is what makes a missing capability noticeable: a row an // admin sees every month with no way to switch it on gets reported, and a row that was never rendered // does not. interface Props { row: SubscriptionRow; draftQuantity: number; changed: boolean; onChange: (key: SubscriptionRowKey, quantity: number) => void; } const priceLine = (row: SubscriptionRow): string => { const per = row.unit === 'block' ? 'per block' : row.unit === 'clinician' ? 'per clinician' : ''; return `${formatMoney(row.unitPriceCents)}/mo${per ? ` ${per}` : ''}`; }; const Meter: React.FC<{ used: number; limit: number; label: string; atLimit: boolean; unit?: string }> = ({ used, limit, label, atLimit, unit }) => ( // The meter rides INLINE with the description (decision 5) rather than sitting in a section of its // own. On telehealth it is doing the conversion work and is a first-class element, not a footnote.
{label} {used} / {limit}{unit ? ` ${unit}` : ''}
); export const SubscriptionRowItem: React.FC = ({ row, draftQuantity, changed, onChange }) => { const enabled = draftQuantity > 0; return (
{row.name} {changed && ( Changed )}

{row.description}

{row.fairUse &&

{row.fairUse}

} {row.includedNote && (

{row.includedNote}

)} {/* Decision 10 — stated on BOTH rows, quietly, never as an error: nothing is wrong. A dependency stated on one side is invisible to whoever reads the other. */} {row.dependencyNote && (

{row.dependencyNote}

)} {/* Decision 9 — the one-time cost is declared BEFORE the toggle, in the attention colour. ⛔ Finding it at checkout reads as a bait. AND IT SAYS WHEN: the server bills it on its own invoice when SMS first turns on, once per carrier registration, never on the monthly bill. */} {/* A SPAN, BECAUSE A PARAGRAPH CANNOT BE AMBER HERE. index.css normalises colour on the root and on p, li, label, input, select, textarea and button at a specificity the utility layer cannot beat, and later in the file - so this line rendered as ordinary body copy. The weight applied and the colour did not, on one of the two sentences about money on this screen. A span is not on that list. */} {row.oneTimeCents !== undefined && !row.enabled && ( A one-time {formatMoney(row.oneTimeCents)} carrier registration is charged on its own invoice when SMS is turned on, unless your current registration is already paid for. )} {/* A line that bills every month while the capability behind it is inert says so HERE, on the row, in the same attention colour as the one-time charge above. It is not an error and it is not the practice's mistake, so it is a statement rather than an alert - but it is about money already being spent, so it is not a muted note either. */} {/* The same rule as the line above, and the same fix: a paragraph here loses its colour to the stylesheet's element normalisation. This one says a line is BILLING every month while the capability behind it is inert, which is the last sentence on the screen that should be drawn as ordinary text. */} {row.notReadyNote && ( {row.notReadyNote} )} {/* MUTED, NOT THE ATTENTION COLOUR. Nothing is wrong and nobody's money is at stake - it is a capability that is not on sale yet, and colouring it like a warning would read as the practice having done something or as the feature being broken. */} {/* A SPAN, AND THAT IS NOT A STYLE PREFERENCE. index.css normalises colour on the root and on p / li / label / input / select / textarea / button at (1,0,1), later in the file than the utility layer - so a `text-...` class on a

here is INERT and this note would have rendered as ordinary body copy. The file says so about itself: "a text- UTILITY still loses to this rule on those eight elements". A span is not on that list, so the muted colour actually lands, and it costs no `!` override. */} {row.unavailableReason && ( {row.unavailableReason} )} {row.meter && }

{priceLine(row)}
{row.unit === 'clinician' && row.unitMultiplier > 1 && enabled && (
{formatMoney(row.unitPriceCents * row.unitMultiplier)}/mo for {row.unitMultiplier}
)}
{row.kind === 'report' ? ( // REPORTS, NEVER CONTROLS (decision 4). Seats follow the clinicians enabled in // Staff; editing them in two places is how the count drifts from what it bills. Manage in Staff ) : row.kind === 'stepper' ? (
{draftQuantity}
) : ( )}
); }; export default SubscriptionRowItem;