claim routes connection done

This commit is contained in:
2025-05-31 23:13:04 +05:30
parent 3faef34917
commit aa38ddc00c
3 changed files with 152 additions and 116 deletions

View File

@@ -2,9 +2,7 @@ import { Router } from "express";
import { Request, Response } from "express"; import { Request, Response } from "express";
import { storage } from "../storage"; import { storage } from "../storage";
import { z } from "zod"; import { z } from "zod";
import { import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
ClaimUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas";
const router = Router(); const router = Router();
@@ -31,6 +29,13 @@ const updateClaimSchema = (
type UpdateClaim = z.infer<typeof updateClaimSchema>; type UpdateClaim = z.infer<typeof updateClaimSchema>;
// Extend the schema to inject `userId` manually (since it's not passed by the client)
const ExtendedClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).extend({
userId: z.number(),
});
// Routes // Routes
// Get all claims for the logged-in user // Get all claims for the logged-in user
@@ -48,11 +53,11 @@ router.get("/:id", async (req: Request, res: Response): Promise<any> => {
try { try {
const idParam = req.params.id; const idParam = req.params.id;
if (!idParam) { if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" }); return res.status(400).json({ error: "Missing claim ID" });
} }
const claimId = parseInt(idParam, 10); const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) { if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" }); return res.status(400).json({ error: "Invalid claim ID" });
} }
const claim = await storage.getClaim(claimId); const claim = await storage.getClaim(claimId);
@@ -73,12 +78,16 @@ router.get("/:id", async (req: Request, res: Response): Promise<any> => {
// Create a new claim // Create a new claim
router.post("/", async (req: Request, res: Response): Promise<any> => { router.post("/", async (req: Request, res: Response): Promise<any> => {
try { try {
const claimData = ClaimSchema.parse({ if (Array.isArray(req.body.serviceLines)) {
req.body.serviceLines = { create: req.body.serviceLines };
}
const parsedClaim = ExtendedClaimSchema.parse({
...req.body, ...req.body,
userId: req.user!.id, userId: req.user!.id,
}); });
const newClaim = await storage.createClaim(claimData); const newClaim = await storage.createClaim(parsedClaim);
res.status(201).json(newClaim); res.status(201).json(newClaim);
} catch (error) { } catch (error) {
if (error instanceof z.ZodError) { if (error instanceof z.ZodError) {
@@ -87,7 +96,14 @@ router.post("/", async (req: Request, res: Response): Promise<any> => {
errors: error.format(), errors: error.format(),
}); });
} }
res.status(500).json({ message: "Failed to create claim" });
console.error("❌ Failed to create claim:", error); // logs full error to server
// Send more detailed info to the client (for dev only)
return res.status(500).json({
message: "Failed to create claim",
error: error instanceof Error ? error.message : String(error),
});
} }
}); });
@@ -96,12 +112,12 @@ router.put("/:id", async (req: Request, res: Response): Promise<any> => {
try { try {
const idParam = req.params.id; const idParam = req.params.id;
if (!idParam) { if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" }); return res.status(400).json({ error: "Missing claim ID" });
} }
const claimId = parseInt(idParam, 10); const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) { if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" }); return res.status(400).json({ error: "Invalid claim ID" });
} }
const existingClaim = await storage.getClaim(claimId); const existingClaim = await storage.getClaim(claimId);
@@ -132,12 +148,12 @@ router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
try { try {
const idParam = req.params.id; const idParam = req.params.id;
if (!idParam) { if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" }); return res.status(400).json({ error: "Missing claim ID" });
} }
const claimId = parseInt(idParam, 10); const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) { if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" }); return res.status(400).json({ error: "Invalid claim ID" });
} }
const existingClaim = await storage.getClaim(claimId); const existingClaim = await storage.getClaim(claimId);

View File

@@ -255,19 +255,18 @@ export function ClaimForm({
}, [patient]); }, [patient]);
useEffect(() => { useEffect(() => {
setForm((prevForm) => { setForm((prevForm) => {
const updatedLines = prevForm.serviceLines.map((line) => ({ const updatedLines = prevForm.serviceLines.map((line) => ({
...line, ...line,
procedureDate: serviceDate, // set all to current serviceDate string procedureDate: serviceDate, // set all to current serviceDate string
})); }));
return { return {
...prevForm, ...prevForm,
serviceLines: updatedLines, serviceLines: updatedLines,
serviceDate, // keep form.serviceDate in sync as well serviceDate, // keep form.serviceDate in sync as well
}; };
}); });
}, [serviceDate]); }, [serviceDate]);
// Handle patient field changes (to make inputs controlled and editable) // Handle patient field changes (to make inputs controlled and editable)
const updatePatientField = (field: keyof Patient, value: any) => { const updatePatientField = (field: keyof Patient, value: any) => {
@@ -293,18 +292,17 @@ export function ClaimForm({
}; };
const updateProcedureDate = (index: number, date: Date | undefined) => { const updateProcedureDate = (index: number, date: Date | undefined) => {
if (!date) return; if (!date) return;
const formattedDate = format(date, "MM/dd/yyyy"); const formattedDate = format(date, "MM/dd/yyyy");
const updatedLines = [...form.serviceLines]; const updatedLines = [...form.serviceLines];
if (updatedLines[index]) { if (updatedLines[index]) {
updatedLines[index].procedureDate = formattedDate; updatedLines[index].procedureDate = formattedDate;
} }
setForm({ ...form, serviceLines: updatedLines });
};
setForm({ ...form, serviceLines: updatedLines });
};
// FILE UPLOAD ZONE // FILE UPLOAD ZONE
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
@@ -335,7 +333,7 @@ export function ClaimForm({
}; };
// Delta MA Button Handler // Delta MA Button Handler
const handleDeltaMASubmit = () => { const handleDeltaMASubmit = async () => {
const appointmentData = { const appointmentData = {
patientId: patientId, patientId: patientId,
date: convertToISODate(serviceDate), date: convertToISODate(serviceDate),
@@ -343,7 +341,7 @@ export function ClaimForm({
}; };
// 1. Create or update appointment // 1. Create or update appointment
onHandleAppointmentSubmit(appointmentData); const appointmentId = await onHandleAppointmentSubmit(appointmentData);
// 2. Update patient // 2. Update patient
if (patient && typeof patient.id === "number") { if (patient && typeof patient.id === "number") {
@@ -368,6 +366,7 @@ export function ClaimForm({
staffId: Number(staff?.id), staffId: Number(staff?.id),
patientId: patient?.id, patientId: patient?.id,
insuranceProvider: "Delta MA", insuranceProvider: "Delta MA",
appointmentId: appointmentId!,
}); });
// 4. Close form // 4. Close form

View File

@@ -124,34 +124,34 @@ export default function ClaimsPage() {
}); });
// Update patient mutation // Update patient mutation
const updatePatientMutation = useMutation({ const updatePatientMutation = useMutation({
mutationFn: async ({ mutationFn: async ({
id, id,
patient, patient,
}: { }: {
id: number; id: number;
patient: UpdatePatient; patient: UpdatePatient;
}) => { }) => {
const res = await apiRequest("PUT", `/api/patients/${id}`, patient); const res = await apiRequest("PUT", `/api/patients/${id}`, patient);
return res.json(); return res.json();
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); queryClient.invalidateQueries({ queryKey: ["/api/patients/"] });
toast({ toast({
title: "Success", title: "Success",
description: "Patient updated successfully!", description: "Patient updated successfully!",
variant: "default", variant: "default",
}); });
}, },
onError: (error) => { onError: (error) => {
toast({ toast({
title: "Error", title: "Error",
description: `Failed to update patient: ${error.message}`, description: `Failed to update patient: ${error.message}`,
variant: "destructive", variant: "destructive",
}); });
}, },
}); });
// Create appointment mutation // Create appointment mutation
const createAppointmentMutation = useMutation({ const createAppointmentMutation = useMutation({
mutationFn: async (appointment: InsertAppointment) => { mutationFn: async (appointment: InsertAppointment) => {
@@ -177,41 +177,41 @@ export default function ClaimsPage() {
}); });
// Update appointment mutation // Update appointment mutation
const updateAppointmentMutation = useMutation({ const updateAppointmentMutation = useMutation({
mutationFn: async ({ mutationFn: async ({
id, id,
appointment, appointment,
}: { }: {
id: number; id: number;
appointment: UpdateAppointment; appointment: UpdateAppointment;
}) => { }) => {
const res = await apiRequest( const res = await apiRequest(
"PUT", "PUT",
`/api/appointments/${id}`, `/api/appointments/${id}`,
appointment appointment
); );
return await res.json(); return await res.json();
}, },
onSuccess: () => { onSuccess: () => {
toast({ toast({
title: "Success", title: "Success",
description: "Appointment updated successfully.", description: "Appointment updated successfully.",
}); });
queryClient.invalidateQueries({ queryKey: ["/api/appointments/all"] }); queryClient.invalidateQueries({ queryKey: ["/api/appointments/all"] });
queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); queryClient.invalidateQueries({ queryKey: ["/api/patients/"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
toast({ toast({
title: "Error", title: "Error",
description: `Failed to update appointment: ${error.message}`, description: `Failed to update appointment: ${error.message}`,
variant: "destructive", variant: "destructive",
}); });
}, },
}); });
const handleAppointmentSubmit = ( const handleAppointmentSubmit = async (
appointmentData: InsertAppointment | UpdateAppointment appointmentData: InsertAppointment | UpdateAppointment
) => { ): Promise<number> => {
// Converts local date to exact UTC date with no offset issues // Converts local date to exact UTC date with no offset issues
function parseLocalDate(dateInput: Date | string): Date { function parseLocalDate(dateInput: Date | string): Date {
if (dateInput instanceof Date) return dateInput; if (dateInput instanceof Date) return dateInput;
@@ -250,23 +250,39 @@ export default function ClaimsPage() {
new Date(a.date).toLocaleDateString("en-CA") === formattedDate new Date(a.date).toLocaleDateString("en-CA") === formattedDate
); );
if (existingAppointment && typeof existingAppointment.id === 'number') { if (existingAppointment && typeof existingAppointment.id === "number") {
// Update appointment with only date // Update appointment with only date
updateAppointmentMutation.mutate({ updateAppointmentMutation.mutate({
id: existingAppointment.id, id: existingAppointment.id,
appointment: minimalData, appointment: minimalData,
}); });
} else { return existingAppointment.id;
// Create new appointment with required fields + defaults
createAppointmentMutation.mutate({
...minimalData,
patientId: appointmentData.patientId,
userId:user?.id,
title: "Scheduled Appointment", // default title
type: "checkup", // default type
} as InsertAppointment);
} }
return new Promise<number>((resolve, reject) => {
createAppointmentMutation.mutate(
{
...minimalData,
patientId: appointmentData.patientId,
userId: user?.id,
title: "Scheduled Appointment",
type: "checkup",
},
{
onSuccess: (newAppointment) => {
resolve(newAppointment.id);
},
onError: (error) => {
toast({
title: "Error",
description: "Could not create appointment",
variant: "destructive",
});
reject(error);
},
}
);
});
}; };
const createClaimMutation = useMutation({ const createClaimMutation = useMutation({
@@ -322,6 +338,15 @@ export default function ClaimsPage() {
patientId: null, patientId: null,
serviceDate: "", serviceDate: "",
}); });
// Remove query parameters without reload
const url = new URL(window.location.href);
url.searchParams.delete("memberId");
url.searchParams.delete("dob");
url.searchParams.delete("name");
// Use history.replaceState to update the URL without reloading
window.history.replaceState({}, document.title, url.toString());
}; };
const prefillClaimForm = (patient: Patient) => { const prefillClaimForm = (patient: Patient) => {
@@ -376,7 +401,7 @@ export default function ClaimsPage() {
}); });
} }
} }
}, [memberId, dob, patients]); }, [memberId, dob]);
function handleClaimSubmit(claimData: any) { function handleClaimSubmit(claimData: any) {
createClaimMutation.mutate(claimData); createClaimMutation.mutate(claimData);
@@ -467,9 +492,7 @@ export default function ClaimsPage() {
onClick={() => { onClick={() => {
if (patientsWithAppointments.length > 0) { if (patientsWithAppointments.length > 0) {
const firstPatient = patientsWithAppointments[0]; const firstPatient = patientsWithAppointments[0];
handleNewClaim( handleNewClaim(Number(firstPatient?.patientId));
Number(firstPatient?.patientId),
);
} else { } else {
toast({ toast({
title: "No patients available", title: "No patients available",
@@ -503,9 +526,7 @@ export default function ClaimsPage() {
<div <div
key={item.patientId} key={item.patientId}
className="py-4 flex items-center justify-between cursor-pointer hover:bg-gray-50" className="py-4 flex items-center justify-between cursor-pointer hover:bg-gray-50"
onClick={() => onClick={() => handleNewClaim(item.patientId)}
handleNewClaim(item.patientId)
}
> >
<div> <div>
<h3 className="font-medium">{item.patientName}</h3> <h3 className="font-medium">{item.patientName}</h3>