feat: United Ucard name+DOB eligibility search via chat, duplicate-record handling, Product ID as member ID
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,10 +113,10 @@ export function UnitedUcardEligibilityButton({
|
||||
const [isSubmittingOtp, setIsSubmittingOtp] = useState(false);
|
||||
|
||||
const handleStart = async () => {
|
||||
if (!memberId || !dateOfBirth) {
|
||||
if (!dateOfBirth || (!memberId && !(firstName && lastName))) {
|
||||
toast({
|
||||
title: "Missing fields",
|
||||
description: "Member ID and Date of Birth are required.",
|
||||
description: "Date of Birth plus either Member ID or First + Last Name are required.",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -231,7 +231,7 @@ export function ChatbotButton() {
|
||||
// Snapshot of insuranceId/dateOfBirth as originally fetched, so we can tell whether the
|
||||
// user hand-edited them in the patient-found card and only PUT to the DB when they did.
|
||||
const originalPatientResultRef = useRef<{ insuranceId: string | null; dateOfBirth: string | null } | null>(null);
|
||||
const [eligibilityIdData, setEligibilityIdData] = useState<{ memberId: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null; appointmentDate?: string | null } | null>(null);
|
||||
const [eligibilityIdData, setEligibilityIdData] = useState<{ memberId: string; firstName?: string; lastName?: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null; appointmentDate?: string | null } | null>(null);
|
||||
const [batchEligibilityData, setBatchEligibilityData] = useState<{ memberId: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null }[] | null>(null);
|
||||
const [checkAndClaimData, setCheckAndClaimData] = useState<CheckAndClaimData | null>(null);
|
||||
const [clarificationData, setClarificationData] = useState<{ memberId: string; dob: string; patient: PatientResult | null; procedureNames: string[]; options: string[] } | null>(null);
|
||||
@@ -562,8 +562,8 @@ export function ChatbotButton() {
|
||||
prefillAndNavigate(patientResult.insuranceId, patientResult.dateOfBirth, getAutoCheck(patientResult.dateOfBirth));
|
||||
};
|
||||
|
||||
const prefillAndNavigate = (memberId: string, dobISO: string, autoCheck: string) => {
|
||||
sessionStorage.setItem("chatbot_eligibility", JSON.stringify({ memberId, dob: dobISO, autoCheck }));
|
||||
const prefillAndNavigate = (memberId: string, dobISO: string, autoCheck: string, names?: { firstName?: string; lastName?: string }) => {
|
||||
sessionStorage.setItem("chatbot_eligibility", JSON.stringify({ memberId, dob: dobISO, autoCheck, firstName: names?.firstName, lastName: names?.lastName }));
|
||||
window.dispatchEvent(new CustomEvent("chatbot:eligibility-prefill"));
|
||||
markJobStarted();
|
||||
if (pendingFolderRenameRef.current) {
|
||||
@@ -598,8 +598,13 @@ export function ChatbotButton() {
|
||||
if (!eligibilityIdData) return;
|
||||
addMsg("user", "Check eligibility now");
|
||||
addMsg("bot", "Opening the eligibility check page...");
|
||||
await syncEditedPatientFields(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.patient?.id);
|
||||
prefillAndNavigate(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.autoCheck);
|
||||
if (eligibilityIdData.memberId) {
|
||||
await syncEditedPatientFields(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.patient?.id);
|
||||
}
|
||||
prefillAndNavigate(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.autoCheck, {
|
||||
firstName: eligibilityIdData.firstName,
|
||||
lastName: eligibilityIdData.lastName,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBatchEligibilityRun = () => {
|
||||
@@ -759,6 +764,8 @@ export function ChatbotButton() {
|
||||
if (data.action === "eligibility_id_ready" && data.actionData) {
|
||||
setEligibilityIdData({
|
||||
memberId: data.actionData.memberId,
|
||||
firstName: data.actionData.firstName,
|
||||
lastName: data.actionData.lastName,
|
||||
dob: data.actionData.dob,
|
||||
siteKey: data.actionData.siteKey,
|
||||
autoCheck: data.actionData.autoCheck,
|
||||
@@ -1134,6 +1141,30 @@ export function ChatbotButton() {
|
||||
className="h-7 text-xs bg-white"
|
||||
/>
|
||||
</div>
|
||||
{eligibilityIdData.firstName !== undefined && (
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[10px] text-gray-500">First Name</Label>
|
||||
<Input
|
||||
value={eligibilityIdData.firstName ?? ""}
|
||||
onChange={(e) =>
|
||||
setEligibilityIdData((prev) => (prev ? { ...prev, firstName: e.target.value } : prev))
|
||||
}
|
||||
className="h-7 text-xs bg-white"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[10px] text-gray-500">Last Name</Label>
|
||||
<Input
|
||||
value={eligibilityIdData.lastName ?? ""}
|
||||
onChange={(e) =>
|
||||
setEligibilityIdData((prev) => (prev ? { ...prev, lastName: e.target.value } : prev))
|
||||
}
|
||||
className="h-7 text-xs bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[10px] text-gray-500">Date of Birth (MM/DD/YYYY)</Label>
|
||||
<Input
|
||||
@@ -1316,6 +1347,8 @@ export function ChatbotButton() {
|
||||
} else if (data.action === "eligibility_id_ready" && data.actionData) {
|
||||
setEligibilityIdData({
|
||||
memberId: data.actionData.memberId,
|
||||
firstName: data.actionData.firstName,
|
||||
lastName: data.actionData.lastName,
|
||||
dob: data.actionData.dob,
|
||||
siteKey: data.actionData.siteKey,
|
||||
autoCheck: data.actionData.autoCheck,
|
||||
|
||||
@@ -115,6 +115,8 @@ export default function InsuranceStatusPage() {
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const isFormIncomplete = !memberId || !dateOfBirth;
|
||||
// United Ucard can also search by first name + last name + DOB when there's no member ID.
|
||||
const isUcardFormIncomplete = !dateOfBirth || (!memberId && !(firstName && lastName));
|
||||
const [isCheckingEligibilityStatus, setIsCheckingEligibilityStatus] =
|
||||
useState(false);
|
||||
const [isCheckingEligibilityAppointment, setIsCheckingEligibilityAppointment] =
|
||||
@@ -186,8 +188,10 @@ export default function InsuranceStatusPage() {
|
||||
const raw = sessionStorage.getItem("chatbot_eligibility");
|
||||
if (!raw) return;
|
||||
try {
|
||||
const { memberId: id, dob, autoCheck: ac } = JSON.parse(raw);
|
||||
const { memberId: id, dob, autoCheck: ac, firstName: fn, lastName: ln } = JSON.parse(raw);
|
||||
if (id) setMemberId(id);
|
||||
if (fn) setFirstName(fn);
|
||||
if (ln) setLastName(ln);
|
||||
if (dob) {
|
||||
// dob may arrive as MM/DD/YYYY or YYYY-MM-DD — normalize to YYYY-MM-DD.
|
||||
// A malformed date (e.g. hand-edited in the chatbot) must not abort the rest of
|
||||
@@ -749,7 +753,8 @@ export default function InsuranceStatusPage() {
|
||||
|
||||
// Auto-trigger after prefill (from chatbot or schedule page "Check Eligibility")
|
||||
useEffect(() => {
|
||||
if (!pendingAutoCheck.current || !memberId || !dateOfBirth) return;
|
||||
if (!pendingAutoCheck.current || !dateOfBirth) return;
|
||||
if (!memberId && !(pendingAutoCheck.current === "united-ucard" && firstName && lastName)) return;
|
||||
const check = pendingAutoCheck.current;
|
||||
pendingAutoCheck.current = null;
|
||||
if (check === "mh" || check === "mh-history") {
|
||||
@@ -1209,7 +1214,7 @@ export default function InsuranceStatusPage() {
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
isFormIncomplete={isUcardFormIncomplete}
|
||||
autoTrigger={triggerTarget === "united-ucard"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={async (pdfId, fallbackFilename) => {
|
||||
|
||||
Reference in New Issue
Block a user