feat: chatbot rendering provider override and NPI provider ordering

- AI chat extracts 'with provider <name>' and routes claim to that provider
- Claim form reads provider from sessionStorage before any async effects run,
  preventing saved claim/procedure data from overriding the chatbot selection
- NPI provider settings table shows Provider #1 / #2 labels with up/down
  reorder buttons; Provider #1 is always the default for claims
- Default provider now uses sortOrder instead of hardcoded 'Mary Scannell'
- Added sortOrder column to NpiProvider schema with migration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-06-11 13:17:05 -04:00
parent d4b9c1b889
commit 75c49ab1df
77 changed files with 385 additions and 105 deletions

View File

@@ -155,6 +155,7 @@ export function ChatbotButton() {
siteKey: string;
serviceDate: string;
appointmentId: number | null;
renderingProvider: string | null;
} | null>(null);
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
const [, setLocation] = useLocation();
@@ -454,13 +455,14 @@ export function ChatbotButton() {
}
if (data.action === "claim_only_ready" && data.actionData) {
const { patient, matchedCodes, siteKey, serviceDate, appointmentId } = data.actionData;
const { patient, matchedCodes, siteKey, serviceDate, appointmentId, renderingProvider } = data.actionData;
setClaimReadyData({
patient: patient ?? null,
matchedCodes: matchedCodes ?? [],
siteKey,
serviceDate,
appointmentId: appointmentId ?? null,
renderingProvider: renderingProvider ?? null,
});
setStep("claim-ready");
return;
@@ -885,13 +887,13 @@ export function ChatbotButton() {
size="sm"
className="flex-1 h-8 text-xs bg-green-600 hover:bg-green-700 text-white"
onClick={() => {
const { patient, matchedCodes, siteKey, serviceDate, appointmentId } = claimReadyData;
const { patient, matchedCodes, siteKey, serviceDate, appointmentId, renderingProvider } = claimReadyData;
addMsg("user", "Confirm & submit claim");
addMsg("bot", "Opening claim...");
if (patient?.id && matchedCodes.length > 0) {
sessionStorage.setItem(
"chatbot_claim_prefill",
JSON.stringify({ codes: matchedCodes, siteKey, serviceDate, autoSubmit: true })
JSON.stringify({ codes: matchedCodes, siteKey, serviceDate, autoSubmit: true, renderingProvider: renderingProvider ?? null })
);
}
setChatbotPendingFiles(pendingFiles);
@@ -956,9 +958,9 @@ export function ChatbotButton() {
const data = await res.json();
replaceLastMsg(data.reply ?? "Sorry, I couldn't process that.");
if (data.action === "claim_only_ready" && data.actionData) {
const { patient, matchedCodes, siteKey, serviceDate, appointmentId } = data.actionData;
const { patient, matchedCodes, siteKey, serviceDate, appointmentId, renderingProvider } = data.actionData;
if (patient?.id && matchedCodes?.length > 0) {
sessionStorage.setItem("chatbot_claim_prefill", JSON.stringify({ codes: matchedCodes, siteKey, serviceDate, autoSubmit: true }));
sessionStorage.setItem("chatbot_claim_prefill", JSON.stringify({ codes: matchedCodes, siteKey, serviceDate, autoSubmit: true, renderingProvider: renderingProvider ?? null }));
}
setChatbotPendingFiles(pendingFiles);
const url = appointmentId ? `/claims?appointmentId=${appointmentId}` : `/claims?newPatient=${patient?.id}`;