fix: D0140 claim sync, schedule column prefill, multi-appt, DentaQuest OTP session
- Fix Express route ordering in appointments-procedures so /prefill-from-appointment is matched before /:id (D0140 and other codes now always reach the claim) - claim-form: always fetch AppointmentProcedure records when an existing claim loads so post-save procedure edits (e.g. adding D0140) are reflected immediately - appointments-page: replace sessionStorage with React state (newApptPrefill) for slot-click prefill so columns B-F correctly carry their staffId into the form - add-appointment-modal / appointment-form: thread prefillData prop; add NewAppointmentPrefill interface; useEffect applies values via setValue - appointments upsert: remove per-patient dedup so the same patient can have multiple appointments on the same day in the same column - DentaQuest / TuftsSCO: navigate to about:blank and minimize instead of quit_driver after each run — session cookie stays in memory so OTP is only required once per app startup, not on every eligibility or claim check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,8 +37,18 @@ import { DateInputField } from "@/components/ui/dateInputField";
|
||||
import { formatLocalDate, parseLocalDate } from "@/utils/dateUtils";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
|
||||
export interface NewAppointmentPrefill {
|
||||
staffId: number;
|
||||
date: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
patientId?: number;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
interface AppointmentFormProps {
|
||||
appointment?: Appointment;
|
||||
prefillData?: NewAppointmentPrefill | null;
|
||||
onSubmit: (data: InsertAppointment | UpdateAppointment) => void;
|
||||
onDelete?: (id: number) => void;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
@@ -47,6 +57,7 @@ interface AppointmentFormProps {
|
||||
|
||||
export function AppointmentForm({
|
||||
appointment,
|
||||
prefillData,
|
||||
onSubmit,
|
||||
onDelete,
|
||||
onOpenChange,
|
||||
@@ -87,19 +98,6 @@ export function AppointmentForm({
|
||||
color: colorMap[staff.name] || "bg-gray-400",
|
||||
}));
|
||||
|
||||
// Get the stored data from session storage
|
||||
const storedDataString = sessionStorage.getItem("newAppointmentData");
|
||||
let parsedStoredData = null;
|
||||
|
||||
// Try to parse it if it exists
|
||||
if (storedDataString) {
|
||||
try {
|
||||
parsedStoredData = JSON.parse(storedDataString);
|
||||
} catch (error) {
|
||||
console.error("Error parsing stored appointment data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Format the date and times for the form
|
||||
const defaultValues: Partial<Appointment> = appointment
|
||||
? {
|
||||
@@ -107,8 +105,8 @@ export function AppointmentForm({
|
||||
patientId: appointment.patientId,
|
||||
title: appointment.title,
|
||||
date: parseLocalDate(appointment.date),
|
||||
startTime: appointment.startTime || "09:00", // Default "09:00"
|
||||
endTime: appointment.endTime || "09:30", // Default "09:30"
|
||||
startTime: appointment.startTime || "09:00",
|
||||
endTime: appointment.endTime || "09:30",
|
||||
type: appointment.type?.startsWith("other:") ? "other" : appointment.type,
|
||||
notes: appointment.notes || "",
|
||||
status: appointment.status || "scheduled",
|
||||
@@ -117,23 +115,18 @@ export function AppointmentForm({
|
||||
? appointment.staffId
|
||||
: undefined,
|
||||
}
|
||||
: parsedStoredData
|
||||
: prefillData
|
||||
? {
|
||||
userId: user?.id,
|
||||
patientId: Number(parsedStoredData.patientId),
|
||||
date: parsedStoredData.date
|
||||
? parseLocalDate(parsedStoredData.date)
|
||||
: parseLocalDate(new Date()),
|
||||
title: parsedStoredData.title || "",
|
||||
startTime: parsedStoredData.startTime,
|
||||
endTime: parsedStoredData.endTime,
|
||||
type: parsedStoredData.type || "checkup",
|
||||
status: parsedStoredData.status || "scheduled",
|
||||
notes: parsedStoredData.notes || "",
|
||||
staffId:
|
||||
typeof parsedStoredData.staff === "number"
|
||||
? parsedStoredData.staff
|
||||
: (staffMembers?.[0]?.id ?? undefined),
|
||||
patientId: prefillData.patientId,
|
||||
date: prefillData.date ? parseLocalDate(prefillData.date) : new Date(),
|
||||
title: "",
|
||||
startTime: prefillData.startTime,
|
||||
endTime: prefillData.endTime,
|
||||
type: prefillData.type || "checkup",
|
||||
status: "scheduled",
|
||||
notes: "",
|
||||
staffId: prefillData.staffId,
|
||||
}
|
||||
: {
|
||||
userId: user?.id ?? 0,
|
||||
@@ -192,67 +185,24 @@ export function AppointmentForm({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectOpen]);
|
||||
|
||||
// Force form field values to update and clean up storage
|
||||
// Prefill form from prefillData prop (new appointment slot click)
|
||||
useEffect(() => {
|
||||
if (!parsedStoredData) return;
|
||||
|
||||
// set times/staff/date as before
|
||||
if (parsedStoredData.startTime)
|
||||
form.setValue("startTime", parsedStoredData.startTime);
|
||||
if (parsedStoredData.endTime)
|
||||
form.setValue("endTime", parsedStoredData.endTime);
|
||||
if (parsedStoredData.staff)
|
||||
form.setValue("staffId", parsedStoredData.staff);
|
||||
if (parsedStoredData.date) {
|
||||
form.setValue("date", parseLocalDate(parsedStoredData.date));
|
||||
if (!prefillData) return;
|
||||
form.setValue("staffId", prefillData.staffId);
|
||||
form.setValue("startTime", prefillData.startTime);
|
||||
form.setValue("endTime", prefillData.endTime);
|
||||
form.setValue("date", parseLocalDate(prefillData.date));
|
||||
if (prefillData.type) form.setValue("type", prefillData.type);
|
||||
if (prefillData.patientId) {
|
||||
form.setValue("patientId", prefillData.patientId);
|
||||
(async () => {
|
||||
try {
|
||||
const res = await apiRequest("GET", `/api/patients/${prefillData.patientId}`);
|
||||
if (res.ok) setPrefillPatient(await res.json());
|
||||
} catch {}
|
||||
})();
|
||||
}
|
||||
|
||||
// ---- patient prefill: check main cache, else fetch once ----
|
||||
if (parsedStoredData.patientId) {
|
||||
const pid = Number(parsedStoredData.patientId);
|
||||
if (!Number.isNaN(pid)) {
|
||||
// ensure the form value is set
|
||||
form.setValue("patientId", pid);
|
||||
|
||||
// fetch single patient record (preferred)
|
||||
(async () => {
|
||||
try {
|
||||
const res = await apiRequest("GET", `/api/patients/${pid}`);
|
||||
if (res.ok) {
|
||||
const patientRecord = await res.json();
|
||||
setPrefillPatient(patientRecord);
|
||||
} else {
|
||||
// non-OK response: show toast with status / message
|
||||
let msg = `Failed to load patient (status ${res.status})`;
|
||||
try {
|
||||
const body = await res.json().catch(() => null);
|
||||
if (body && body.message) msg = body.message;
|
||||
} catch {}
|
||||
toast({
|
||||
title: "Could not load patient",
|
||||
description: msg,
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: "Error fetching patient",
|
||||
description:
|
||||
(err as Error)?.message ||
|
||||
"An unknown error occurred while fetching patient details.",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
// remove the one-time transport
|
||||
sessionStorage.removeItem("newAppointmentData");
|
||||
}
|
||||
})();
|
||||
}
|
||||
} else {
|
||||
// no patientId in storage — still remove to avoid stale state
|
||||
sessionStorage.removeItem("newAppointmentData");
|
||||
}
|
||||
}, [form]);
|
||||
}, [prefillData]);
|
||||
|
||||
// When editing an appointment, ensure we prefill the patient so SelectValue can render
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user