From aa38ddc00c526c8b07f4f7f762f8361f21618525 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 31 May 2025 23:13:04 +0530 Subject: [PATCH] claim routes connection done --- apps/Backend/src/routes/claims.ts | 40 ++-- .../src/components/claims/claim-form.tsx | 47 +++-- apps/Frontend/src/pages/claims-page.tsx | 181 ++++++++++-------- 3 files changed, 152 insertions(+), 116 deletions(-) diff --git a/apps/Backend/src/routes/claims.ts b/apps/Backend/src/routes/claims.ts index 3e222cd..8fd3c54 100644 --- a/apps/Backend/src/routes/claims.ts +++ b/apps/Backend/src/routes/claims.ts @@ -2,9 +2,7 @@ import { Router } from "express"; import { Request, Response } from "express"; import { storage } from "../storage"; import { z } from "zod"; -import { - ClaimUncheckedCreateInputObjectSchema, -} from "@repo/db/usedSchemas"; +import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; const router = Router(); @@ -31,6 +29,13 @@ const updateClaimSchema = ( type UpdateClaim = z.infer; +// Extend the schema to inject `userId` manually (since it's not passed by the client) +const ExtendedClaimSchema = ( + ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject +).extend({ + userId: z.number(), +}); + // Routes // Get all claims for the logged-in user @@ -48,11 +53,11 @@ router.get("/:id", async (req: Request, res: Response): Promise => { try { const idParam = req.params.id; 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); 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); @@ -73,12 +78,16 @@ router.get("/:id", async (req: Request, res: Response): Promise => { // Create a new claim router.post("/", async (req: Request, res: Response): Promise => { try { - const claimData = ClaimSchema.parse({ + if (Array.isArray(req.body.serviceLines)) { + req.body.serviceLines = { create: req.body.serviceLines }; + } + + const parsedClaim = ExtendedClaimSchema.parse({ ...req.body, userId: req.user!.id, }); - const newClaim = await storage.createClaim(claimData); + const newClaim = await storage.createClaim(parsedClaim); res.status(201).json(newClaim); } catch (error) { if (error instanceof z.ZodError) { @@ -87,7 +96,14 @@ router.post("/", async (req: Request, res: Response): Promise => { 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 => { try { const idParam = req.params.id; 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); 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); @@ -132,12 +148,12 @@ router.delete("/:id", async (req: Request, res: Response): Promise => { try { const idParam = req.params.id; 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); 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); diff --git a/apps/Frontend/src/components/claims/claim-form.tsx b/apps/Frontend/src/components/claims/claim-form.tsx index 21fff14..e3df3fe 100644 --- a/apps/Frontend/src/components/claims/claim-form.tsx +++ b/apps/Frontend/src/components/claims/claim-form.tsx @@ -255,19 +255,18 @@ export function ClaimForm({ }, [patient]); useEffect(() => { - setForm((prevForm) => { - const updatedLines = prevForm.serviceLines.map((line) => ({ - ...line, - procedureDate: serviceDate, // set all to current serviceDate string - })); - return { - ...prevForm, - serviceLines: updatedLines, - serviceDate, // keep form.serviceDate in sync as well - }; - }); -}, [serviceDate]); - + setForm((prevForm) => { + const updatedLines = prevForm.serviceLines.map((line) => ({ + ...line, + procedureDate: serviceDate, // set all to current serviceDate string + })); + return { + ...prevForm, + serviceLines: updatedLines, + serviceDate, // keep form.serviceDate in sync as well + }; + }); + }, [serviceDate]); // Handle patient field changes (to make inputs controlled and editable) const updatePatientField = (field: keyof Patient, value: any) => { @@ -293,18 +292,17 @@ export function ClaimForm({ }; const updateProcedureDate = (index: number, date: Date | undefined) => { - if (!date) return; + if (!date) return; - const formattedDate = format(date, "MM/dd/yyyy"); - const updatedLines = [...form.serviceLines]; + const formattedDate = format(date, "MM/dd/yyyy"); + const updatedLines = [...form.serviceLines]; - if (updatedLines[index]) { - updatedLines[index].procedureDate = formattedDate; - } - - setForm({ ...form, serviceLines: updatedLines }); -}; + if (updatedLines[index]) { + updatedLines[index].procedureDate = formattedDate; + } + setForm({ ...form, serviceLines: updatedLines }); + }; // FILE UPLOAD ZONE const [uploadedFiles, setUploadedFiles] = useState([]); @@ -335,7 +333,7 @@ export function ClaimForm({ }; // Delta MA Button Handler - const handleDeltaMASubmit = () => { + const handleDeltaMASubmit = async () => { const appointmentData = { patientId: patientId, date: convertToISODate(serviceDate), @@ -343,7 +341,7 @@ export function ClaimForm({ }; // 1. Create or update appointment - onHandleAppointmentSubmit(appointmentData); + const appointmentId = await onHandleAppointmentSubmit(appointmentData); // 2. Update patient if (patient && typeof patient.id === "number") { @@ -368,6 +366,7 @@ export function ClaimForm({ staffId: Number(staff?.id), patientId: patient?.id, insuranceProvider: "Delta MA", + appointmentId: appointmentId!, }); // 4. Close form diff --git a/apps/Frontend/src/pages/claims-page.tsx b/apps/Frontend/src/pages/claims-page.tsx index efe6cb8..13d564a 100644 --- a/apps/Frontend/src/pages/claims-page.tsx +++ b/apps/Frontend/src/pages/claims-page.tsx @@ -124,34 +124,34 @@ export default function ClaimsPage() { }); // Update patient mutation - const updatePatientMutation = useMutation({ - mutationFn: async ({ - id, - patient, - }: { - id: number; - patient: UpdatePatient; - }) => { - const res = await apiRequest("PUT", `/api/patients/${id}`, patient); - return res.json(); - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); - toast({ - title: "Success", - description: "Patient updated successfully!", - variant: "default", - }); - }, - onError: (error) => { - toast({ - title: "Error", - description: `Failed to update patient: ${error.message}`, - variant: "destructive", - }); - }, - }); - + const updatePatientMutation = useMutation({ + mutationFn: async ({ + id, + patient, + }: { + id: number; + patient: UpdatePatient; + }) => { + const res = await apiRequest("PUT", `/api/patients/${id}`, patient); + return res.json(); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); + toast({ + title: "Success", + description: "Patient updated successfully!", + variant: "default", + }); + }, + onError: (error) => { + toast({ + title: "Error", + description: `Failed to update patient: ${error.message}`, + variant: "destructive", + }); + }, + }); + // Create appointment mutation const createAppointmentMutation = useMutation({ mutationFn: async (appointment: InsertAppointment) => { @@ -177,41 +177,41 @@ export default function ClaimsPage() { }); // Update appointment mutation - const updateAppointmentMutation = useMutation({ - mutationFn: async ({ - id, - appointment, - }: { - id: number; - appointment: UpdateAppointment; - }) => { - const res = await apiRequest( - "PUT", - `/api/appointments/${id}`, - appointment - ); - return await res.json(); - }, - onSuccess: () => { - toast({ - title: "Success", - description: "Appointment updated successfully.", - }); - queryClient.invalidateQueries({ queryKey: ["/api/appointments/all"] }); - queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); - }, - onError: (error: Error) => { - toast({ - title: "Error", - description: `Failed to update appointment: ${error.message}`, - variant: "destructive", - }); - }, - }); - - const handleAppointmentSubmit = ( + const updateAppointmentMutation = useMutation({ + mutationFn: async ({ + id, + appointment, + }: { + id: number; + appointment: UpdateAppointment; + }) => { + const res = await apiRequest( + "PUT", + `/api/appointments/${id}`, + appointment + ); + return await res.json(); + }, + onSuccess: () => { + toast({ + title: "Success", + description: "Appointment updated successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/appointments/all"] }); + queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); + }, + onError: (error: Error) => { + toast({ + title: "Error", + description: `Failed to update appointment: ${error.message}`, + variant: "destructive", + }); + }, + }); + + const handleAppointmentSubmit = async ( appointmentData: InsertAppointment | UpdateAppointment - ) => { + ): Promise => { // Converts local date to exact UTC date with no offset issues function parseLocalDate(dateInput: Date | string): Date { if (dateInput instanceof Date) return dateInput; @@ -250,23 +250,39 @@ export default function ClaimsPage() { 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 updateAppointmentMutation.mutate({ id: existingAppointment.id, appointment: minimalData, }); - } else { - // 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 existingAppointment.id; } + + return new Promise((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({ @@ -322,6 +338,15 @@ export default function ClaimsPage() { patientId: null, 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) => { @@ -376,7 +401,7 @@ export default function ClaimsPage() { }); } } - }, [memberId, dob, patients]); + }, [memberId, dob]); function handleClaimSubmit(claimData: any) { createClaimMutation.mutate(claimData); @@ -467,9 +492,7 @@ export default function ClaimsPage() { onClick={() => { if (patientsWithAppointments.length > 0) { const firstPatient = patientsWithAppointments[0]; - handleNewClaim( - Number(firstPatient?.patientId), - ); + handleNewClaim(Number(firstPatient?.patientId)); } else { toast({ title: "No patients available", @@ -503,9 +526,7 @@ export default function ClaimsPage() {
- handleNewClaim(item.patientId) - } + onClick={() => handleNewClaim(item.patientId)} >

{item.patientName}