fix: make chatbot patient-found DOB/Member ID editable and fix eligibility→appointment ordering

- Member ID and DOB in the AI chat "patient found" confirmation card are now
  editable inputs, and edits are persisted back to the patient record before
  proceeding so corrections don't revert to stale values.
- Fix auto-lookup-by-member-ID effect on the insurance status page clobbering
  a hand-edited DOB with the stale DB value during chatbot prefill.
- CMSP (under-14) eligibility flow was missing the appointment-creation hook
  that the adult MassHealth flow already had, so under-14 patients never got
  scheduled after a chatbot-initiated "eligibility & appointment" request.
- For already-known patients, the chatbot was creating the appointment before
  running the eligibility check instead of after; now eligibility is always
  checked first and the appointment created only afterward, and only when the
  resulting patient status is ACTIVE.
This commit is contained in:
2026-07-20 20:05:13 -04:00
parent b30bed8903
commit ca568de817
2 changed files with 88 additions and 50 deletions
@@ -173,6 +173,9 @@ export default function InsuranceStatusPage() {
const [triggerTarget, setTriggerTarget] = useState<string | null>(null);
const pendingScrollTo = useRef<string | null>(null);
const [prefillTick, setPrefillTick] = useState(0);
// Set when the chatbot prefill supplies a DOB, so the auto-lookup-by-member-ID effect below
// doesn't clobber a deliberately hand-edited DOB with the (possibly stale/wrong) one on file.
const suppressAutoLookupDobRef = useRef(false);
// Prefill from chatbot
useEffect(() => {
@@ -192,6 +195,7 @@ export default function InsuranceStatusPage() {
? (() => { const [m, d, y] = dob.split("/"); return `${y}-${m!.padStart(2,"0")}-${d!.padStart(2,"0")}`; })()
: dob;
setDateOfBirth(parseLocalDate(normalized));
suppressAutoLookupDobRef.current = true;
} catch (dobErr) {
console.error("[insurance-status] failed to parse prefilled DOB:", dob, dobErr);
toast({
@@ -254,11 +258,15 @@ export default function InsuranceStatusPage() {
if (patient) {
setFirstName(patient.firstName ?? "");
setLastName(patient.lastName ?? "");
const dob =
typeof patient.dateOfBirth === "string"
? parseLocalDate(patient.dateOfBirth)
: patient.dateOfBirth ?? null;
setDateOfBirth(dob);
if (suppressAutoLookupDobRef.current) {
suppressAutoLookupDobRef.current = false;
} else {
const dob =
typeof patient.dateOfBirth === "string"
? parseLocalDate(patient.dateOfBirth)
: patient.dateOfBirth ?? null;
setDateOfBirth(dob);
}
}
} catch {
// silently ignore lookup errors
@@ -709,6 +717,9 @@ export default function InsuranceStatusPage() {
variant: "default",
});
const claimed = await tryClaimFromChatbot(selectedPatient?.id);
void tryAppointmentFromChatbot();
if (claimed) return;
setSelectedPatient(null);
await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
processNextInQueue();
@@ -831,6 +842,17 @@ export default function InsuranceStatusPage() {
const patient = await lookupRes.json();
if (!patient?.id) return;
// The eligibility check that just ran saved the result to patient.status — only book
// the appointment when insurance came back ACTIVE.
if (patient.status !== "ACTIVE") {
toast({
title: "No appointment created",
description: `${patient.firstName ?? ""} ${patient.lastName ?? ""} insurance is ${patient.status === "INACTIVE" ? "inactive" : "not active"} — please add manually if needed.`.trim(),
variant: "destructive",
});
return;
}
const apptRes = await apiRequest("POST", "/api/ai/create-appointment-today", { patientId: patient.id, date: useDate ?? undefined });
const apptData = await apptRes.json();
if (apptRes.ok) {