import { isValidPhoneNumber } from './phoneNumber'; import type { AbsenceReason } from './patientFieldAbsence'; /** * - WHEN THE FRONT DESK MAY SUBMIT A REGISTRATION. * * **THIS MIRRORS THE SERVER'S `patientCreateRequirements`, DELIBERATELY AND VISIBLY.** The two live in * different repositories and cannot share code, so the thing that keeps them honest is that both are pure, * both are tested against the same table of cases, and both are written to the same rule: * * an email, OR a recorded reason for having none - never both, never neither * and on the no-email path: a birth date, plus a phone or an address * * **The client is NOT the enforcement.** The server refuses an empty string as firmly as a missing * field. What this buys is that a receptionist is told BEFORE they submit, rather than being handed a 400 * after typing a whole form - and that they are never offered a path the server will reject. * * **The point of the whole feature: nobody should ever have to invent a fake email to get past the * form.** A fabricated address is worse than a blank one - unreachable, and looking reachable. */ export interface RegistrationFields { firstName: string; lastName: string; email: string; emailAbsenceReason: string; phone: string; birthDate: string; line1: string; city: string; postalCode: string; } export const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export const hasPostalAddress = (form: Pick): boolean => Boolean(form.line1.trim() || form.city.trim() || form.postalCode.trim()); /** * Enough to find this person again, when their email cannot be the key. * * Email is the strongest dedup key we have and matching is GLOBAL - so without it we need a birth date * and one reachable contact. That contact is also **the only thing a later claim can be challenged * against**: a record with no email, no phone and no address can never be handed to the person it * describes. */ export const identifiableWithoutEmail = (form: RegistrationFields): boolean => Boolean(form.birthDate) && (isValidPhoneNumber(form.phone) || hasPostalAddress(form)); export const canRegisterPatient = (form: RegistrationFields): boolean => { if (!form.firstName.trim() || !form.lastName.trim()) return false; if (form.emailAbsenceReason) { // A record cannot be both reachable and unreachable. The server refuses the contradiction, so // the form must never build one. if (form.email.trim()) return false; return identifiableWithoutEmail(form); } return ( EMAIL_PATTERN.test(form.email.trim()) && isValidPhoneNumber(form.phone) && Boolean(form.birthDate) ); }; /** The three codes the server accepts. A fourth invented here would be refused at the door. */ export const EMAIL_ABSENCE_CODES = [ 'not-applicable', 'asked-declined', 'asked-unknown', ] as const satisfies readonly AbsenceReason[];