// The vocabulary for `Address.state`. FHIR binds that element to nothing at all, so a garbage state // is a valid resource and no layer below the input can refuse one - the list has to live here. // // Not to be confused with the driver's-license issuer list in `FhirIdentifier.tsx`, which is keyed by // OID and answers a different question (which authority issued this document). export interface UsState { /** The USPS abbreviation, which is what `Address.state` stores. */ code: string; name: string; } // Territories are included because a patient can genuinely live in one and their post is addressed // the same way. export const US_STATES: readonly UsState[] = [ { code: "AL", name: "Alabama" }, { code: "AK", name: "Alaska" }, { code: "AZ", name: "Arizona" }, { code: "AR", name: "Arkansas" }, { code: "AS", name: "American Samoa" }, { code: "CA", name: "California" }, { code: "CO", name: "Colorado" }, { code: "CT", name: "Connecticut" }, { code: "DE", name: "Delaware" }, { code: "DC", name: "District of Columbia" }, { code: "FL", name: "Florida" }, { code: "GA", name: "Georgia" }, { code: "GU", name: "Guam" }, { code: "HI", name: "Hawaii" }, { code: "ID", name: "Idaho" }, { code: "IL", name: "Illinois" }, { code: "IN", name: "Indiana" }, { code: "IA", name: "Iowa" }, { code: "KS", name: "Kansas" }, { code: "KY", name: "Kentucky" }, { code: "LA", name: "Louisiana" }, { code: "ME", name: "Maine" }, { code: "MD", name: "Maryland" }, { code: "MA", name: "Massachusetts" }, { code: "MI", name: "Michigan" }, { code: "MN", name: "Minnesota" }, { code: "MS", name: "Mississippi" }, { code: "MO", name: "Missouri" }, { code: "MP", name: "Northern Mariana Islands" }, { code: "MT", name: "Montana" }, { code: "NE", name: "Nebraska" }, { code: "NV", name: "Nevada" }, { code: "NH", name: "New Hampshire" }, { code: "NJ", name: "New Jersey" }, { code: "NM", name: "New Mexico" }, { code: "NY", name: "New York" }, { code: "NC", name: "North Carolina" }, { code: "ND", name: "North Dakota" }, { code: "OH", name: "Ohio" }, { code: "OK", name: "Oklahoma" }, { code: "OR", name: "Oregon" }, { code: "PA", name: "Pennsylvania" }, { code: "PR", name: "Puerto Rico" }, { code: "RI", name: "Rhode Island" }, { code: "SC", name: "South Carolina" }, { code: "SD", name: "South Dakota" }, { code: "TN", name: "Tennessee" }, { code: "TX", name: "Texas" }, { code: "UT", name: "Utah" }, { code: "VT", name: "Vermont" }, { code: "VA", name: "Virginia" }, { code: "VI", name: "U.S. Virgin Islands" }, { code: "WA", name: "Washington" }, { code: "WV", name: "West Virginia" }, { code: "WI", name: "Wisconsin" }, { code: "WY", name: "Wyoming" }, ]; const BY_CODE = new Map(US_STATES.map((s) => [s.code, s])); const BY_NAME = new Map(US_STATES.map((s) => [s.name.toLowerCase(), s])); export const isUsStateCode = (value: string | undefined): boolean => Boolean(value && BY_CODE.has(value.trim().toUpperCase())); export const usStateName = (code: string | undefined): string | undefined => code ? BY_CODE.get(code.trim().toUpperCase())?.name : undefined; /** * A stored value -> its canonical code, or `undefined` when it is not a state at all. * * Accepts a full name as well as a code because records arrive from imports and from other systems, * not only from this form - "Iowa" is a real answer to "what state", and silently rejecting it would * make an already-correct record look broken. */ export const normalizeUsState = (raw: string | undefined): string | undefined => { const trimmed = (raw ?? "").trim(); if (!trimmed) return undefined; return ( BY_CODE.get(trimmed.toUpperCase())?.code ?? BY_NAME.get(trimmed.toLowerCase())?.code ); }; /** * Keeps a US postal code typeable and nothing else - digits, and the single hyphen of ZIP+4. * * Deliberately a filter rather than a rule that rejects on save: it cannot reach a state where an * existing bad value blocks the screen, because the worst it does is decline to add a character. */ export const sanitizeUsPostalCode = (raw: string): string => { const digits = raw.replace(/[^0-9]/g, "").slice(0, 9); return digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits; };