feat: auto-confirm option for AI column claims (manual toggle + implied by Auto Claim)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 00:07:30 -04:00
co-authored by Claude Fable 5
parent 41bed21302
commit efb85d0b92
@@ -199,6 +199,11 @@ export default function AppointmentsPage() {
const [isClaimingAiColumn, setIsClaimingAiColumn] = useState(false); const [isClaimingAiColumn, setIsClaimingAiColumn] = useState(false);
const [autoClaimEnabled, setAutoClaimEnabled] = useState(false); const [autoClaimEnabled, setAutoClaimEnabled] = useState(false);
const [autoClaimTime, setAutoClaimTime] = useState("19:00"); const [autoClaimTime, setAutoClaimTime] = useState("19:00");
const [autoConfirmEnabled, setAutoConfirmEnabled] = useState(
() => localStorage.getItem("ai_claim_auto_confirm") === "1"
);
// True while the current AI claim run should advance without manual Confirm clicks
const [aiClaimAutoRun, setAiClaimAutoRun] = useState(false);
const autoClaimFiredHourRef = useRef<number | null>(null); const autoClaimFiredHourRef = useRef<number | null>(null);
const [aiClaimModalOpen, setAiClaimModalOpen] = useState(false); const [aiClaimModalOpen, setAiClaimModalOpen] = useState(false);
const [aiClaimQueue, setAiClaimQueue] = useState<ScheduledAppointment[]>([]); const [aiClaimQueue, setAiClaimQueue] = useState<ScheduledAppointment[]>([]);
@@ -488,6 +493,7 @@ export default function AppointmentsPage() {
sessionStorage.setItem("ai_claim_queue", JSON.stringify({ ...parsed, pendingResume: false })); sessionStorage.setItem("ai_claim_queue", JSON.stringify({ ...parsed, pendingResume: false }));
setAiClaimQueue(queue); setAiClaimQueue(queue);
setAiClaimCurrentIndex(0); setAiClaimCurrentIndex(0);
setAiClaimAutoRun(!!parsed.autoRun);
setAiClaimModalOpen(true); setAiClaimModalOpen(true);
processAiClaimAtIndex(queue, 0); processAiClaimAtIndex(queue, 0);
} catch { } catch {
@@ -1652,13 +1658,16 @@ export default function AppointmentsPage() {
toast({ title: "No unclaimed appointments", description: "All appointments in the selected columns are already claimed." }); toast({ title: "No unclaimed appointments", description: "All appointments in the selected columns are already claimed." });
return; return;
} }
const autoRun = autoConfirmEnabled || autoClaimEnabled;
sessionStorage.setItem("ai_claim_queue", JSON.stringify({ sessionStorage.setItem("ai_claim_queue", JSON.stringify({
appointments: unclaimed, appointments: unclaimed,
date: formattedSelectedDate, date: formattedSelectedDate,
pendingResume: false, pendingResume: false,
autoRun,
})); }));
setAiClaimQueue(unclaimed); setAiClaimQueue(unclaimed);
setAiClaimCurrentIndex(0); setAiClaimCurrentIndex(0);
setAiClaimAutoRun(autoRun);
setAiClaimModalOpen(true); setAiClaimModalOpen(true);
await processAiClaimAtIndex(unclaimed, 0); await processAiClaimAtIndex(unclaimed, 0);
}; };
@@ -1673,6 +1682,7 @@ export default function AppointmentsPage() {
appointments: fromCurrent, appointments: fromCurrent,
date: formattedSelectedDate, date: formattedSelectedDate,
pendingResume: true, pendingResume: true,
autoRun: aiClaimAutoRun,
})); }));
} else { } else {
sessionStorage.removeItem("ai_claim_queue"); sessionStorage.removeItem("ai_claim_queue");
@@ -1760,6 +1770,28 @@ export default function AppointmentsPage() {
} }
}; };
// Auto mode: advance the AI claim queue without manual Confirm clicks.
// Confirms when codes matched, skips otherwise (incl. CDT clarifications, which need human input).
useEffect(() => {
if (!aiClaimModalOpen || !aiClaimAutoRun || isAiClaimProcessing) return;
if (aiClaimCdtClarification) {
const t = setTimeout(() => {
setAiClaimCdtClarification(null);
handleAiClaimSkip();
}, 1500);
return () => clearTimeout(t);
}
if (!aiClaimCurrentData) return;
const t = setTimeout(() => {
if (aiClaimCurrentData.matchedCodes.length > 0) {
handleAiClaimConfirm();
} else {
handleAiClaimSkip();
}
}, 1500);
return () => clearTimeout(t);
}, [aiClaimModalOpen, aiClaimAutoRun, isAiClaimProcessing, aiClaimCurrentData, aiClaimCdtClarification]);
return ( return (
<div> <div>
<SeleniumTaskBanner <SeleniumTaskBanner
@@ -1907,6 +1939,19 @@ export default function AppointmentsPage() {
</span> </span>
</label> </label>
))} ))}
<div className="flex items-center gap-2 border-l pl-3 ml-1">
<Switch
id="auto-confirm-toggle"
checked={autoConfirmEnabled}
onCheckedChange={(v) => {
setAutoConfirmEnabled(v);
localStorage.setItem("ai_claim_auto_confirm", v ? "1" : "0");
}}
/>
<Label htmlFor="auto-confirm-toggle" className="text-sm font-medium cursor-pointer whitespace-nowrap">
Auto Confirm
</Label>
</div>
<div className="flex items-center gap-2 border-l pl-3 ml-1"> <div className="flex items-center gap-2 border-l pl-3 ml-1">
<Switch <Switch
id="auto-claim-toggle" id="auto-claim-toggle"
@@ -2485,6 +2530,9 @@ export default function AppointmentsPage() {
AI Claim Queue AI Claim Queue
</h3> </h3>
<span className="text-xs text-gray-500 font-medium"> <span className="text-xs text-gray-500 font-medium">
{aiClaimAutoRun && (
<span className="text-teal-600 mr-2">Auto</span>
)}
{aiClaimCurrentIndex + 1} / {aiClaimQueue.length} {aiClaimCurrentIndex + 1} / {aiClaimQueue.length}
</span> </span>
</div> </div>