feat: reschedule-by-office batch SMS, AI follow-up toggle, date-shortcut fix, combined flow diagram

- Add Reschedule for Column button on schedule page with AI follow-up toggle (default on)
- Add POST /api/twilio/send-reschedule-batch — sends Reschedule by Office template, starts AI reschedule conversation per patient
- Add {officePhone} (office call-in number) and {twilioPhone} (SMS number) variable replacement in both batch endpoints
- Fix broken variable names in Reschedule by Office template ({office phone number) → {officePhone}, {Twilio phone number} → {twilioPhone})
- Fix reschedule-graph: when patient replies with date in same message as YES/NO (e.g. "ok, 5/18"), AI now checks day open and asks for time instead of asking "what day and time?"
- Fix twilio-webhooks: same date-shortcut logic for reminder flow — "no, 5/18" skips "when to reschedule?" and goes straight to day check
- Update LangGraph SVG: rename to Reminder & Reschedule Flow, combine both entry points (Reminder SMS + Reschedule SMS) into one diagram with date-shortcut annotations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-05-13 00:36:38 -04:00
parent 7929dc6e19
commit 131733564e
5 changed files with 371 additions and 78 deletions

View File

@@ -293,6 +293,20 @@ router.post("/webhook/sms", async (req: Request, res: Response): Promise<any> =>
await saveOutbound(patient.id, introText);
}
// If patient said "no" but already included a date (e.g. "no, 5/18"),
// skip "when to reschedule?" and go straight to date processing
if (intent === "no") {
const hasDateInMessage =
/\b\d{1,2}[\/\-]\d{1,2}\b/.test(Body) ||
/\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow|next week)\b/i.test(Body);
if (hasDateInMessage) {
const { reply: rescheduleReply, nextStage: rescheduleNextStage } = await runRescheduleStep(
Body, "asked_reschedule_datetime", language, patient.id, aiSettings.apiKey, patient.userId
);
return reply(rescheduleReply, rescheduleNextStage);
}
}
// Send message 2 (yes/no response) via TwiML — queued SECOND
return reply(intentReply, nextStage);
}
@@ -313,6 +327,20 @@ router.post("/webhook/sms", async (req: Request, res: Response): Promise<any> =>
if (intent === "no") nextStage = "asked_reschedule_datetime";
else if (intent === "wants_appointment") nextStage = "asked_new_or_existing";
else nextStage = "done";
// If patient said "no" but already included a date, skip straight to date processing
if (intent === "no") {
const hasDateInMessage =
/\b\d{1,2}[\/\-]\d{1,2}\b/.test(Body) ||
/\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow|next week)\b/i.test(Body);
if (hasDateInMessage) {
const { reply: rescheduleReply, nextStage: rescheduleNextStage } = await runRescheduleStep(
Body, "asked_reschedule_datetime", language, patient.id, aiSettings.apiKey, patient.userId
);
return reply(rescheduleReply, rescheduleNextStage);
}
}
return reply(aiReply, nextStage);
}
}