- Shipped
- August 27, 2026 at 8:43 PM UTC
- Author
- kamo
- Commit
- d5f44ac
The formatter every phone surface shared kept the LAST TEN DIGITS of whatever it was handed and dressed them as a NANP number, so "+44 20 7946 0958" rendered as "(207) 946-0958" — a different number, in a different country, that a person could read off the screen and dial. `CreateLeadClient` had its own copy that was worse: it truncated at ten digits, so an international number typed into a lead was mutilated on entry and the original was gone. `app/lib/phone.ts` now formats through libphonenumber-js: display, as-you-type entry, E.164 for the wire, and a length check for validation. Comparison stays hand-rolled — a last-ten-digit match is all the reverse lookup needs, and it has to keep working on the junk a phone system produces (four-digit extensions, feature codes, an empty caller ID on a Local channel leg). That property is still pinned: anything that is not a whole number comes back untouched. Entry goes through `PhoneField` (MUI) or `BarePhoneInput` (for the surfaces that style their own inputs), both over `usePhoneInput`, which exists for the two things that make an as-you-type field usable rather than infuriating: the caret stays with the digit just typed instead of being flung to the end by the reformat, and backspacing over a separator deletes a digit — naively it deletes nothing, because the formatter puts the separator straight back. Forms that already collect a country pass it through, so a UK org's own number is grouped the way the UK groups it rather than against a US template. Gate on `isPossible()`, not `isValid()`: the min metadata rejects reserved exchanges, and gating on validity would stop formatting every 555 number, which is most of a demo org. Also fixed along the way: the "international" field-mapping transform emitted '+' + digits, a plus glued to a national number, which is some other country's number; and the SMS dialpad counted characters rather than digits, so a seven-digit number dressed up as "(949) 374-7" passed its ten-character gate. Deliberately NOT formatted: the phone-instance config fields and the VoIP outbound SMS number. Those are provider configuration and want raw E.164.