diff --git a/apps/Backend/src/routes/patients.ts b/apps/Backend/src/routes/patients.ts index 6fc81b7..936d0db 100644 --- a/apps/Backend/src/routes/patients.ts +++ b/apps/Backend/src/routes/patients.ts @@ -144,7 +144,7 @@ router.get("/search", async (req: Request, res: Response): Promise => { } const [patients, totalCount] = await Promise.all([ - storage.searchPatients({ + storage.searchPatients({ filters, limit: parseInt(limit), offset: parseInt(offset), @@ -201,6 +201,18 @@ router.post("/", async (req: Request, res: Response): Promise => { userId: req.user!.id, }); + // Check for duplicate insuranceId if it's provided + if (patientData.insuranceId) { + const existingPatient = await storage.getPatientByInsuranceId( + patientData.insuranceId + ); + + if (existingPatient) { + return res.status(409).json({ + message: "A patient with this insurance ID already exists.", + }); + } + } const patient = await storage.createPatient(patientData); @@ -244,8 +256,26 @@ router.put( // Validate request body const patientData = updatePatientSchema.parse(req.body); + // If updating insuranceId, check for uniqueness (excluding self) + if ( + patientData.insuranceId && + patientData.insuranceId !== existingPatient.insuranceId + ) { + const duplicatePatient = await storage.getPatientByInsuranceId( + patientData.insuranceId + ); + if (duplicatePatient && duplicatePatient.id !== patientId) { + return res.status(409).json({ + message: "Another patient with this insurance ID already exists.", + }); + } + } + // Update patient - const updatedPatient = await storage.updatePatient(patientId, patientData); + const updatedPatient = await storage.updatePatient( + patientId, + patientData + ); res.json(updatedPatient); } catch (error) { if (error instanceof z.ZodError) { diff --git a/apps/Backend/src/storage/index.ts b/apps/Backend/src/storage/index.ts index a9eb2d9..a3d0a38 100644 --- a/apps/Backend/src/storage/index.ts +++ b/apps/Backend/src/storage/index.ts @@ -165,6 +165,7 @@ export interface IStorage { // Patient methods getPatient(id: number): Promise; + getPatientByInsuranceId(insuranceId: string): Promise; getPatientsByUserId(userId: number): Promise; getRecentPatients(limit: number, offset: number): Promise; getTotalPatientCount(): Promise; @@ -315,6 +316,12 @@ export const storage: IStorage = { return await db.patient.findMany({ where: { userId } }); }, + async getPatientByInsuranceId(insuranceId: string): Promise { + return db.patient.findFirst({ + where: { insuranceId }, + }); + }, + async getRecentPatients(limit: number, offset: number): Promise { return db.patient.findMany({ skip: offset, diff --git a/apps/Frontend/src/lib/queryClient.ts b/apps/Frontend/src/lib/queryClient.ts index 332deb6..69f4cf9 100644 --- a/apps/Frontend/src/lib/queryClient.ts +++ b/apps/Frontend/src/lib/queryClient.ts @@ -4,7 +4,6 @@ const API_BASE_URL = import.meta.env.VITE_API_BASE_URL_BACKEND ?? ""; async function throwIfResNotOk(res: Response) { if (!res.ok) { - if (res.status === 401 || res.status === 403) { localStorage.removeItem("token"); if (!window.location.pathname.startsWith("/auth")) { @@ -13,7 +12,19 @@ async function throwIfResNotOk(res: Response) { } return; } - throw new Error(`${res.status}: ${res.statusText}`); + + // Try to parse the response as JSON for a more meaningful error message + let message = `${res.status}: ${res.statusText}`; + try { + const errorBody = await res.json(); + if (errorBody?.message) { + message = errorBody.message; + } + } catch { + // ignore JSON parse errors, keep default message + } + + throw new Error(message); } } diff --git a/apps/Frontend/src/pages/insurance-eligibility-page.tsx b/apps/Frontend/src/pages/insurance-eligibility-page.tsx index 9fba52d..bc44aff 100644 --- a/apps/Frontend/src/pages/insurance-eligibility-page.tsx +++ b/apps/Frontend/src/pages/insurance-eligibility-page.tsx @@ -26,6 +26,7 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; +import { apiRequest, queryClient } from "@/lib/queryClient"; const PatientSchema = ( PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject @@ -34,6 +35,15 @@ const PatientSchema = ( }); type Patient = z.infer; +const insertPatientSchema = ( + PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject +).omit({ + id: true, + createdAt: true, + userId: true, +}); +type InsertPatient = z.infer; + export default function InsuranceEligibilityPage() { const { user } = useAuth(); const { toast } = useToast(); @@ -53,23 +63,55 @@ export default function InsuranceEligibilityPage() { // Populate fields from selected patient useEffect(() => { - if (selectedPatient) { - setMemberId(selectedPatient.insuranceId ?? ""); - setFirstName(selectedPatient.firstName ?? ""); - setLastName(selectedPatient.lastName ?? ""); + if (selectedPatient) { + setMemberId(selectedPatient.insuranceId ?? ""); + setFirstName(selectedPatient.firstName ?? ""); + setLastName(selectedPatient.lastName ?? ""); - const dob = selectedPatient.dateOfBirth - ? new Date(selectedPatient.dateOfBirth) - : undefined; - setDateOfBirth(dob); - } else { - setMemberId(""); - setFirstName(""); - setLastName(""); - setDateOfBirth(undefined); - } -}, [selectedPatient]); + const dob = selectedPatient.dateOfBirth + ? new Date(selectedPatient.dateOfBirth) + : undefined; + setDateOfBirth(dob); + } else { + setMemberId(""); + setFirstName(""); + setLastName(""); + setDateOfBirth(undefined); + } + }, [selectedPatient]); + // Add patient mutation + const addPatientMutation = useMutation({ + mutationFn: async (patient: InsertPatient) => { + const res = await apiRequest("POST", "/api/patients/", patient); + return res.json(); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["patients"] }); + toast({ + title: "Success", + description: "Patient added successfully!", + variant: "default", + }); + }, + onError: (error: any) => { + const msg = error.message; + + if (msg === "A patient with this insurance ID already exists.") { + toast({ + title: "Patient already exists", + description: msg, + variant: "default", + }); + } else { + toast({ + title: "Error", + description: `Failed to add patient: ${msg}`, + variant: "destructive", + }); + } + }, + }); // Insurance eligibility check mutation const checkInsuranceMutation = useMutation({ @@ -114,16 +156,34 @@ export default function InsuranceEligibilityPage() { }); // Handle insurance provider button clicks - const handleMHButton= () => { + const handleMHButton = () => { + // Form Fields check if (!memberId || !dateOfBirth || !firstName) { - toast({ - title: "Missing Fields", - description: - "Please fill in all the required fields: Member ID, Date of Birth, First Name.", - variant: "destructive", - }); - return; - } + toast({ + title: "Missing Fields", + description: + "Please fill in all the required fields: Member ID, Date of Birth, First Name.", + variant: "destructive", + }); + return; + } + + // Adding patient if same patient exists then it will skip. + const newPatient: InsertPatient = { + firstName, + lastName, + dateOfBirth: dateOfBirth, + gender: "", + phone: "", + userId: user?.id ?? 1, + status: "active", + insuranceId: memberId, + }; + addPatientMutation.mutate(newPatient); + + + + }; return (