feat: AI chat scheduling, claim automation, and session improvements

- Internal AI chat: schedule_appointment intent books earliest available
  slot in Column A using office hours; claim_only intent looks up latest
  past appointment for service date, asks user when two appointments are
  within 7 days, auto-triggers correct Selenium worker with mapped prices
- Gemini model updated to gemini-flash-latest; conversation history (15
  messages) passed for pronoun/reference resolution; history trimmed to
  start with user turn so Gemini doesn't reject the context
- Insurance alias file (insuranceAliases.json) replaces hardcoded siteKey
  matching; "tufs" now resolves to TUFTS_SCO
- DOB format normalized (MM/DD/YYYY → YYYY-MM-DD) before parseLocalDate;
  autoCheck now fires for all insurance types, not just MH/CMSP
- Claim form auto-submit: all handlers (MH, CCA, DDMA, UnitedDH, Tufts)
  accept formToUse and receive fee-schedule-priced form; prefillDone set
  after chatbot code prefill so autoSubmit gate opens correctly
- Chatbot: chat history persisted in sessionStorage, cleared on logout
  and auto-logout; Clear button writes fresh state synchronously; message
  history window increased to 15
- DentaQuest/TuftsSCO Selenium: "Remember me" checkbox clicked before
  sign-in to persist OTP trust cookie across sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-05 16:19:56 -04:00
co-authored by Claude Sonnet 4.6
parent ba2882957a
commit 1bbca38344
11 changed files with 693 additions and 94 deletions
+16 -4
View File
@@ -38,9 +38,8 @@ import { useLicense } from "@/hooks/use-license";
export default function ClaimsPage() {
const [isClaimFormOpen, setIsClaimFormOpen] = useState(false);
const [selectedPatientId, setSelectedPatientId] = useState<number | null>(
null
);
const [selectedPatientId, setSelectedPatientId] = useState<number | null>(null);
const [chatbotAutoSubmitSiteKey, setChatbotAutoSubmitSiteKey] = useState<string | undefined>(undefined);
// for redirect from appointment page directly, then passing to claimform
const [selectedAppointmentId, setSelectedAppointmentId] = useState<
number | null
@@ -239,6 +238,17 @@ export default function ClaimsPage() {
const id = Number(newPatient);
if (!Number.isFinite(id) || id <= 0) return;
// Check if chatbot requested auto-submit with a specific insurance
try {
const raw = sessionStorage.getItem("chatbot_claim_prefill");
if (raw) {
const parsed = JSON.parse(raw);
if (parsed?.autoSubmit && parsed?.siteKey) {
setChatbotAutoSubmitSiteKey(parsed.siteKey);
}
}
} catch {}
handleNewClaim(id);
clearUrlParams(["newPatient"]);
}, [newPatient]);
@@ -786,6 +796,7 @@ export default function ClaimsPage() {
const closeClaim = () => {
setSelectedPatientId(null);
setSelectedAppointmentId(null);
setChatbotAutoSubmitSiteKey(undefined);
setIsClaimFormOpen(false);
clearUrlParams(["newPatient", "appointmentId"]);
@@ -904,7 +915,8 @@ export default function ClaimsPage() {
<ClaimForm
patientId={selectedPatientId}
appointmentId={selectedAppointmentId ?? undefined}
autoSubmit={mode === "direct"}
autoSubmit={mode === "direct" || !!chatbotAutoSubmitSiteKey}
autoSubmitSiteKey={chatbotAutoSubmitSiteKey}
proceduresOnly={mode === "procedures"}
onClose={closeClaim}
onSubmit={handleClaimSubmit}
@@ -179,8 +179,14 @@ export default function InsuranceStatusPage() {
try {
const { memberId: id, dob, autoCheck: ac } = JSON.parse(raw);
if (id) setMemberId(id);
if (dob) setDateOfBirth(parseLocalDate(dob));
if (ac === "mh" || ac === "cmsp") pendingAutoCheck.current = ac;
if (dob) {
// dob may arrive as MM/DD/YYYY or YYYY-MM-DD — normalize to YYYY-MM-DD
const normalized = /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dob)
? (() => { const [m, d, y] = dob.split("/"); return `${y}-${m!.padStart(2,"0")}-${d!.padStart(2,"0")}`; })()
: dob;
setDateOfBirth(parseLocalDate(normalized));
}
if (ac) pendingAutoCheck.current = ac;
sessionStorage.removeItem("chatbot_eligibility");
} catch {}
};