diff --git a/apps/Backend/src/routes/claims.ts b/apps/Backend/src/routes/claims.ts index bebca4d..e863a37 100644 --- a/apps/Backend/src/routes/claims.ts +++ b/apps/Backend/src/routes/claims.ts @@ -4,7 +4,7 @@ import { storage } from "../storage"; import { z } from "zod"; import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; import multer from "multer"; -import { forwardToSeleniumAgent } from "../services/seleniumClient"; +import { forwardToSeleniumAgent } from "../services/seleniumClaimClient"; import path from "path"; import axios from "axios"; import fs from "fs"; diff --git a/apps/Backend/src/routes/patients.ts b/apps/Backend/src/routes/patients.ts index a131f0d..32280a4 100644 --- a/apps/Backend/src/routes/patients.ts +++ b/apps/Backend/src/routes/patients.ts @@ -68,6 +68,24 @@ router.get("/", async (req, res) => { } }); +// Get recent patients (paginated) +router.get("/recent", async (req: Request, res: Response) => { + try { + const limit = parseInt(req.query.limit as string) || 10; + const offset = parseInt(req.query.offset as string) || 0; + + const [patients, totalCount] = await Promise.all([ + storage.getRecentPatients(limit, offset), + storage.getTotalPatientCount(), + ]); + + res.json({ patients, totalCount }); + } catch (error) { + console.error("Failed to retrieve recent patients:", error); + res.status(500).json({ message: "Failed to retrieve recent patients" }); + } +}); + // Get a single patient by ID router.get( "/:id", @@ -101,19 +119,6 @@ router.get( } ); -// Get recent patients (paginated) -router.get("/recent", async (req: Request, res: Response) => { - try { - const limit = parseInt(req.query.limit as string) || 10; - const offset = parseInt(req.query.offset as string) || 0; - - const recentPatients = await storage.getRecentPatients(limit, offset); - res.json(recentPatients); - } catch (error) { - console.error("Failed to retrieve recent patients:", error); - res.status(500).json({ message: "Failed to retrieve recent patients" }); - } -}); // Create a new patient router.post("/", async (req: Request, res: Response): Promise => { diff --git a/apps/Backend/src/storage/index.ts b/apps/Backend/src/storage/index.ts index 435402e..637e8f6 100644 --- a/apps/Backend/src/storage/index.ts +++ b/apps/Backend/src/storage/index.ts @@ -167,6 +167,7 @@ export interface IStorage { getPatient(id: number): Promise; getPatientsByUserId(userId: number): Promise; getRecentPatients(limit: number, offset: number): Promise; + getTotalPatientCount(): Promise; createPatient(patient: InsertPatient): Promise; updatePatient(id: number, patient: UpdatePatient): Promise; deletePatient(id: number): Promise; @@ -304,6 +305,10 @@ export const storage: IStorage = { }); }, + async getTotalPatientCount(): Promise { + return db.patient.count(); + }, + async createPatient(patient: InsertPatient): Promise { return await db.patient.create({ data: patient as Patient }); }, @@ -371,7 +376,7 @@ export const storage: IStorage = { return db.appointment.findMany({ skip: offset, take: limit, - orderBy: { date: "desc" } + orderBy: { date: "desc" }, }); }, @@ -589,7 +594,7 @@ export const storage: IStorage = { id: true, filename: true, uploadedAt: true, - patient:true, + patient: true, }, }); }, diff --git a/apps/Frontend/src/components/patients/add-patient-modal.tsx b/apps/Frontend/src/components/patients/add-patient-modal.tsx index 26f16f7..94af3fd 100644 --- a/apps/Frontend/src/components/patients/add-patient-modal.tsx +++ b/apps/Frontend/src/components/patients/add-patient-modal.tsx @@ -74,7 +74,6 @@ export const AddPatientModal = forwardRef< >(function AddPatientModal(props, ref) { const { open, onOpenChange, onSubmit, isLoading, patient, extractedInfo } = props; - const { toast } = useToast(); const [formData, setFormData] = useState< InsertPatient | UpdatePatient | null >(null); diff --git a/apps/Frontend/src/components/patients/patient-form.tsx b/apps/Frontend/src/components/patients/patient-form.tsx index 375c904..11aa9a3 100644 --- a/apps/Frontend/src/components/patients/patient-form.tsx +++ b/apps/Frontend/src/components/patients/patient-form.tsx @@ -338,11 +338,12 @@ export const PatientForm = forwardRef( Select provider - Delta Dental - MetLife - Cigna - Aetna - Other + Mass Health + Delta MA + MetLife + Cigna + Aetna + Other None diff --git a/apps/Frontend/src/components/patients/patient-table.tsx b/apps/Frontend/src/components/patients/patient-table.tsx index 7be78fe..88fe4da 100644 --- a/apps/Frontend/src/components/patients/patient-table.tsx +++ b/apps/Frontend/src/components/patients/patient-table.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useMemo, useState } from "react"; import { Table, TableBody, @@ -7,14 +7,8 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; import { Button } from "@/components/ui/button"; -import { Delete, Edit, Eye, MoreVertical } from "lucide-react"; +import { Delete, Edit, Eye } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { @@ -26,56 +20,224 @@ import { PaginationPrevious, } from "@/components/ui/pagination"; import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; -import {z} from "zod"; +import { z } from "zod"; +import { apiRequest, queryClient } from "@/lib/queryClient"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import LoadingScreen from "../ui/LoadingScreen"; +import { useToast } from "@/hooks/use-toast"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { AddPatientModal } from "./add-patient-modal"; +import { DeleteConfirmationDialog } from "../ui/deleteDialog"; +import { useAuth } from "@/hooks/use-auth"; -const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ +const PatientSchema = ( + PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject +).omit({ appointments: true, }); type Patient = z.infer; -interface PatientTableProps { +const updatePatientSchema = ( + PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject +) + .omit({ + id: true, + createdAt: true, + userId: true, + }) + .partial(); + +type UpdatePatient = z.infer; + + +interface PatientApiResponse { patients: Patient[]; - onEdit: (patient: Patient) => void; - onView: (patient: Patient) => void; - onDelete: (patient: Patient) => void; + totalCount: number; } -export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTableProps) { +interface PatientTableProps { + allowEdit?: boolean; + allowView?: boolean; + allowDelete?: boolean; +} + +export function PatientTable({ + allowEdit, + allowView, + allowDelete, +}: PatientTableProps) { + const { toast } = useToast(); + const { user } = useAuth(); + + + const [isAddPatientOpen, setIsAddPatientOpen] = useState(false); + const [isViewPatientOpen, setIsViewPatientOpen] = useState(false); + const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false); + const [currentPatient, setCurrentPatient] = useState( + undefined + ); + const [currentPage, setCurrentPage] = useState(1); const patientsPerPage = 5; + const offset = (currentPage - 1) * patientsPerPage; + + const { + data: patientsData, + isLoading, + isError, + } = useQuery({ + queryKey: ["patients", currentPage], + queryFn: async () => { + const res = await apiRequest( + "GET", + `/api/patients/recent?limit=${patientsPerPage}&offset=${offset}` + ); + return res.json(); + }, + placeholderData: { + patients: [], + totalCount: 0, + }, + }); + + // 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: () => { + setIsAddPatientOpen(false); + queryClient.invalidateQueries({ queryKey: ["patients", currentPage] }); + toast({ + title: "Success", + description: "Patient updated successfully!", + variant: "default", + }); + }, + onError: (error) => { + toast({ + title: "Error", + description: `Failed to update patient: ${error.message}`, + variant: "destructive", + }); + }, + }); - // Get current patients - const indexOfLastPatient = currentPage * patientsPerPage; - const indexOfFirstPatient = indexOfLastPatient - patientsPerPage; - const currentPatients = patients.slice(indexOfFirstPatient, indexOfLastPatient); - const totalPages = Math.ceil(patients.length / patientsPerPage); + const deletePatientMutation = useMutation({ + mutationFn: async (id: number) => { + const res = await apiRequest("DELETE", `/api/patients/${id}`); + return; + }, + onSuccess: () => { + setIsDeletePatientOpen(false); + queryClient.invalidateQueries({ queryKey: ["patients", currentPage] }); + toast({ + title: "Success", + description: "Patient deleted successfully!", + variant: "default", + }); + }, + onError: (error) => { + console.log(error); + toast({ + title: "Error", + description: `Failed to delete patient: ${error.message}`, + variant: "destructive", + }); + }, + }); + + const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => { + if (currentPatient && user) { + const { id, ...sanitizedPatient } = patient; + updatePatientMutation.mutate({ + id: currentPatient.id, + patient: sanitizedPatient, + }); + } else { + console.error("No current patient or user found for update"); + toast({ + title: "Error", + description: "Cannot update patient: No patient or user found", + variant: "destructive", + }); + } + }; + + const handleEditPatient = (patient: Patient) => { + setCurrentPatient(patient); + setIsAddPatientOpen(true); + }; + + const handleViewPatient = (patient: Patient) => { + setCurrentPatient(patient); + setIsViewPatientOpen(true); + }; + + const handleDeletePatient = (patient: Patient) => { + setCurrentPatient(patient); + setIsDeletePatientOpen(true); + }; + + const handleConfirmDeletePatient = async () => { + if (currentPatient) { + deletePatientMutation.mutate(currentPatient.id); + } else { + toast({ + title: "Error", + description: "No patient selected for deletion.", + variant: "destructive", + }); + } + }; + + const totalPages = useMemo( + () => Math.ceil((patientsData?.totalCount || 0) / patientsPerPage), + [patientsData] + ); + const startItem = offset + 1; + const endItem = Math.min( + offset + patientsPerPage, + patientsData?.totalCount || 0 + ); const getInitials = (firstName: string, lastName: string) => { return (firstName.charAt(0) + lastName.charAt(0)).toUpperCase(); }; const getAvatarColor = (id: number) => { - const colorClasses = [ - "bg-blue-500", - "bg-teal-500", - "bg-amber-500", - "bg-rose-500", - "bg-indigo-500", - "bg-green-500", - "bg-purple-500", - ]; - - // This returns a literal string from above — not a generated string - return colorClasses[id % colorClasses.length]; -}; + const colorClasses = [ + "bg-blue-500", + "bg-teal-500", + "bg-amber-500", + "bg-rose-500", + "bg-indigo-500", + "bg-green-500", + "bg-purple-500", + ]; + return colorClasses[id % colorClasses.length]; + }; const formatDate = (dateString: string | Date) => { const date = new Date(dateString); - return new Intl.DateTimeFormat('en-US', { - day: '2-digit', - month: 'short', - year: 'numeric' + return new Intl.DateTimeFormat("en-US", { + day: "2-digit", + month: "short", + year: "numeric", }).format(date); }; @@ -94,29 +256,52 @@ export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTabl - {currentPatients.length === 0 ? ( + {isLoading ? ( - - No patients found. Add your first patient to get started. + + + + + ) : isError ? ( + + + Error loading patients. + + + ) : patientsData?.patients.length === 0 ? ( + + + No patients found. ) : ( - currentPatients.map((patient) => ( + patientsData?.patients.map((patient) => (
- + {getInitials(patient.firstName, patient.lastName)} - +
{patient.firstName} {patient.lastName}
- PID-{patient.id.toString().padStart(4, '0')} + PID-{patient.id.toString().padStart(4, "0")}
@@ -135,21 +320,7 @@ export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTabl
- {patient.insuranceProvider ? ( - patient.insuranceProvider === 'delta' - ? 'Delta Dental' - : patient.insuranceProvider === 'metlife' - ? 'MetLife' - : patient.insuranceProvider === 'cigna' - ? 'Cigna' - : patient.insuranceProvider === 'aetna' - ? 'Aetna' - : patient.insuranceProvider === 'none' - ? 'No Insurance' - : patient.insuranceProvider - ) : ( - 'Not specified' - )} + {patient.insuranceProvider ?? "Not specified"}
{patient.insuranceId && (
@@ -159,7 +330,9 @@ export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTabl {patient.status} @@ -167,33 +340,43 @@ export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTabl
- - - + {allowDelete && ( + + )} + {allowEdit && ( + + )} + {allowView && ( + + )}
@@ -202,33 +385,205 @@ export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTabl
- - {patients.length > patientsPerPage && ( -
-
-
-

- Showing {indexOfFirstPatient + 1} to{" "} - - {Math.min(indexOfLastPatient, patients.length)} - {" "} - of {patients.length} results -

+ + {/* View Patient Modal */} + + + + Patient Details + + Complete information about the patient. + + + + {currentPatient && ( +
+
+
+ {currentPatient.firstName.charAt(0)} + {currentPatient.lastName.charAt(0)} +
+
+

+ {currentPatient.firstName} {currentPatient.lastName} +

+

+ Patient ID: {currentPatient.id.toString().padStart(4, "0")} +

+
+
+ +
+
+

+ Personal Information +

+
+

+ Date of Birth:{" "} + {new Date( + currentPatient.dateOfBirth + ).toLocaleDateString()} +

+

+ Gender:{" "} + {currentPatient.gender.charAt(0).toUpperCase() + + currentPatient.gender.slice(1)} +

+

+ Status:{" "} + + {currentPatient.status.charAt(0).toUpperCase() + + currentPatient.status.slice(1)} + +

+
+
+ +
+

+ Contact Information +

+
+

+ Phone:{" "} + {currentPatient.phone} +

+

+ Email:{" "} + {currentPatient.email || "N/A"} +

+

+ Address:{" "} + {currentPatient.address ? ( + <> + {currentPatient.address} + {currentPatient.city && `, ${currentPatient.city}`} + {currentPatient.zipCode && + ` ${currentPatient.zipCode}`} + + ) : ( + "N/A" + )} +

+
+
+ +
+

Insurance

+
+

+ Provider:{" "} + {currentPatient.insuranceProvider + ? currentPatient.insuranceProvider === "delta" + ? "Delta Dental" + : currentPatient.insuranceProvider === "metlife" + ? "MetLife" + : currentPatient.insuranceProvider === "cigna" + ? "Cigna" + : currentPatient.insuranceProvider === "aetna" + ? "Aetna" + : currentPatient.insuranceProvider + : "N/A"} +

+

+ ID:{" "} + {currentPatient.insuranceId || "N/A"} +

+

+ Group Number:{" "} + {currentPatient.groupNumber || "N/A"} +

+

+ Policy Holder:{" "} + {currentPatient.policyHolder || "Self"} +

+
+
+ +
+

+ Medical Information +

+
+

+ Allergies:{" "} + {currentPatient.allergies || "None reported"} +

+

+ Medical Conditions:{" "} + {currentPatient.medicalConditions || "None reported"} +

+
+
+
+ +
+ + +
+
+ )} +
+
+ + {/* Add/Edit Patient Modal */} + + + setIsDeletePatientOpen(false)} + entityName={currentPatient?.name} + /> + + {/* Pagination */} + {totalPages > 1 && ( +
+
+
+ Showing {startItem}–{endItem} of {patientsData?.totalCount || 0}{" "} + results
- - { e.preventDefault(); if (currentPage > 1) setCurrentPage(currentPage - 1); }} - className={currentPage === 1 ? "pointer-events-none opacity-50" : ""} + className={ + currentPage === 1 ? "pointer-events-none opacity-50" : "" + } /> - + {Array.from({ length: totalPages }).map((_, i) => ( ))} - - { e.preventDefault(); - if (currentPage < totalPages) setCurrentPage(currentPage + 1); + if (currentPage < totalPages) + setCurrentPage(currentPage + 1); }} - className={currentPage === totalPages ? "pointer-events-none opacity-50" : ""} + className={ + currentPage === totalPages + ? "pointer-events-none opacity-50" + : "" + } /> diff --git a/apps/Frontend/src/pages/dashboard.tsx b/apps/Frontend/src/pages/dashboard.tsx index 56310bc..543ba14 100644 --- a/apps/Frontend/src/pages/dashboard.tsx +++ b/apps/Frontend/src/pages/dashboard.tsx @@ -16,7 +16,6 @@ import { AppointmentsByDay } from "@/components/analytics/appointments-by-day"; import { NewPatients } from "@/components/analytics/new-patients"; import { AppointmentUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; - import { Users, Calendar, @@ -26,15 +25,7 @@ import { Clock, } from "lucide-react"; import { Link } from "wouter"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; import { z } from "zod"; -import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog"; //creating types out of schema auto generated. type Appointment = z.infer; @@ -72,17 +63,6 @@ const insertPatientSchema = ( }); type InsertPatient = z.infer; -const updatePatientSchema = ( - PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject -) - .omit({ - id: true, - createdAt: true, - userId: true, - }) - .partial(); - -type UpdatePatient = z.infer; // Type for the ref to access modal methods type AddPatientModalRef = { @@ -93,8 +73,6 @@ type AddPatientModalRef = { export default function Dashboard() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isAddPatientOpen, setIsAddPatientOpen] = useState(false); - const [isViewPatientOpen, setIsViewPatientOpen] = useState(false); - const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false); const [isAddAppointmentOpen, setIsAddAppointmentOpen] = useState(false); const [currentPatient, setCurrentPatient] = useState( undefined @@ -160,60 +138,6 @@ export default function Dashboard() { }, }); - // 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: () => { - setIsAddPatientOpen(false); - 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 deletePatientMutation = useMutation({ - mutationFn: async (id: number) => { - const res = await apiRequest("DELETE", `/api/patients/${id}`); - return; - }, - onSuccess: () => { - setIsDeletePatientOpen(false); - queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); - toast({ - title: "Success", - description: "Patient deleted successfully!", - variant: "default", - }); - }, - onError: (error) => { - console.log(error); - toast({ - title: "Error", - description: `Failed to delete patient: ${error.message}`, - variant: "destructive", - }); - }, - }); - const toggleMobileMenu = () => { setIsMobileMenuOpen(!isMobileMenuOpen); }; @@ -227,54 +151,9 @@ export default function Dashboard() { } }; - const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => { - if (currentPatient && user) { - const { id, ...sanitizedPatient } = patient; - updatePatientMutation.mutate({ - id: currentPatient.id, - patient: sanitizedPatient, - }); - } else { - console.error("No current patient or user found for update"); - toast({ - title: "Error", - description: "Cannot update patient: No patient or user found", - variant: "destructive", - }); - } - }; - - const handleEditPatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsAddPatientOpen(true); - }; - - const handleViewPatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsViewPatientOpen(true); - }; - - const handleDeletePatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsDeletePatientOpen(true); - }; - - const handleConfirmDeletePatient = async () => { - if (currentPatient) { - deletePatientMutation.mutate(currentPatient.id); - } else { - toast({ - title: "Error", - description: "No patient selected for deletion.", - variant: "destructive", - }); - } - }; - const isLoading = isLoadingPatients || - addPatientMutation.isPending || - updatePatientMutation.isPending; + addPatientMutation.isPending; // Create appointment mutation const createAppointmentMutation = useMutation({ @@ -570,17 +449,9 @@ export default function Dashboard() { {/* Patient Table */} - - setIsDeletePatientOpen(false)} - entityName={currentPatient?.name} + allowDelete={true} + allowEdit={true} + allowView={true} />
@@ -591,187 +462,11 @@ export default function Dashboard() { ref={addPatientModalRef} open={isAddPatientOpen} onOpenChange={setIsAddPatientOpen} - onSubmit={currentPatient ? handleUpdatePatient : handleAddPatient} + onSubmit={handleAddPatient} isLoading={isLoading} patient={currentPatient} /> - {/* View Patient Modal */} - - - - Patient Details - - Complete information about the patient. - - - - {currentPatient && ( -
-
-
- {currentPatient.firstName.charAt(0)} - {currentPatient.lastName.charAt(0)} -
-
-

- {currentPatient.firstName} {currentPatient.lastName} -

-

- Patient ID: {currentPatient.id.toString().padStart(4, "0")} -

-
-
- -
-
-

- Personal Information -

-
-

- {currentPatient.dateOfBirth ? ( - (() => { - const dobDate = parseISO(currentPatient.dateOfBirth); - return isValid(dobDate) ? ( - - - Date of Birth: - {" "} - {format(dobDate, "PPP")} - - ) : ( - - Date of Birth: N/A - - ); - })() - ) : ( - - Date of Birth: N/A - - )} -

-

- Gender:{" "} - {currentPatient.gender.charAt(0).toUpperCase() + - currentPatient.gender.slice(1)} -

-

- Status:{" "} - - {currentPatient.status.charAt(0).toUpperCase() + - currentPatient.status.slice(1)} - -

-
-
- -
-

- Contact Information -

-
-

- Phone:{" "} - {currentPatient.phone} -

-

- Email:{" "} - {currentPatient.email || "N/A"} -

-

- Address:{" "} - {currentPatient.address ? ( - <> - {currentPatient.address} - {currentPatient.city && `, ${currentPatient.city}`} - {currentPatient.zipCode && - ` ${currentPatient.zipCode}`} - - ) : ( - "N/A" - )} -

-
-
- -
-

Insurance

-
-

- Provider:{" "} - {currentPatient.insuranceProvider - ? currentPatient.insuranceProvider === "delta" - ? "Delta Dental" - : currentPatient.insuranceProvider === "metlife" - ? "MetLife" - : currentPatient.insuranceProvider === "cigna" - ? "Cigna" - : currentPatient.insuranceProvider === "aetna" - ? "Aetna" - : currentPatient.insuranceProvider - : "N/A"} -

-

- ID:{" "} - {currentPatient.insuranceId || "N/A"} -

-

- Group Number:{" "} - {currentPatient.groupNumber || "N/A"} -

-

- Policy Holder:{" "} - {currentPatient.policyHolder || "Self"} -

-
-
- -
-

- Medical Information -

-
-

- Allergies:{" "} - {currentPatient.allergies || "None reported"} -

-

- Medical Conditions:{" "} - {currentPatient.medicalConditions || "None reported"} -

-
-
-
- -
- - -
-
- )} -
-
- {/* Add/Edit Appointment Modal */}
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
diff --git a/apps/Frontend/src/pages/patients-page.tsx b/apps/Frontend/src/pages/patients-page.tsx index a7c3cf4..1926d27 100644 --- a/apps/Frontend/src/pages/patients-page.tsx +++ b/apps/Frontend/src/pages/patients-page.tsx @@ -23,14 +23,6 @@ import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useAuth } from "@/hooks/use-auth"; import { z } from "zod"; -import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; import useExtractPdfData from "@/hooks/use-extractPdfData"; import { useLocation } from "wouter"; @@ -50,18 +42,6 @@ const insertPatientSchema = ( }); type InsertPatient = z.infer; -const updatePatientSchema = ( - PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject -) - .omit({ - id: true, - createdAt: true, - userId: true, - }) - .partial(); - -type UpdatePatient = z.infer; - // Type for the ref to access modal methods type AddPatientModalRef = { shouldSchedule: boolean; @@ -72,8 +52,6 @@ export default function PatientsPage() { const { toast } = useToast(); const { user } = useAuth(); const [isAddPatientOpen, setIsAddPatientOpen] = useState(false); - const [isViewPatientOpen, setIsViewPatientOpen] = useState(false); - const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false); const [currentPatient, setCurrentPatient] = useState( undefined ); @@ -90,20 +68,6 @@ export default function PatientsPage() { const { mutate: extractPdf } = useExtractPdfData(); const [location, navigate] = useLocation(); - // Fetch patients - const { - data: patients = [], - isLoading: isLoadingPatients, - refetch: refetchPatients, - } = useQuery({ - queryKey: ["/api/patients/"], - queryFn: async () => { - const res = await apiRequest("GET", "/api/patients/"); - return res.json(); - }, - enabled: !!user, - }); - // Add patient mutation const addPatientMutation = useMutation({ mutationFn: async (patient: InsertPatient) => { @@ -133,60 +97,6 @@ export default function PatientsPage() { }, }); - // 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: () => { - setIsAddPatientOpen(false); - 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 deletePatientMutation = useMutation({ - mutationFn: async (id: number) => { - const res = await apiRequest("DELETE", `/api/patients/${id}`); - return; - }, - onSuccess: () => { - setIsDeletePatientOpen(false); - queryClient.invalidateQueries({ queryKey: ["/api/patients/"] }); - toast({ - title: "Success", - description: "Patient deleted successfully!", - variant: "default", - }); - }, - onError: (error) => { - console.log(error); - toast({ - title: "Error", - description: `Failed to delete patient: ${error.message}`, - variant: "destructive", - }); - }, - }); - const toggleMobileMenu = () => { setIsMobileMenuOpen(!isMobileMenuOpen); }; @@ -200,54 +110,7 @@ export default function PatientsPage() { } }; - const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => { - if (currentPatient && user) { - const { id, ...sanitizedPatient } = patient; - updatePatientMutation.mutate({ - id: currentPatient.id, - patient: sanitizedPatient, - }); - } else { - console.error("No current patient or user found for update"); - toast({ - title: "Error", - description: "Cannot update patient: No patient or user found", - variant: "destructive", - }); - } - }; - - const handleEditPatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsAddPatientOpen(true); - }; - - const handleViewPatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsViewPatientOpen(true); - }; - - const handleDeletePatient = (patient: Patient) => { - setCurrentPatient(patient); - setIsDeletePatientOpen(true); - }; - - const handleConfirmDeletePatient = async () => { - if (currentPatient) { - deletePatientMutation.mutate(currentPatient.id); - } else { - toast({ - title: "Error", - description: "No patient selected for deletion.", - variant: "destructive", - }); - } - }; - - const isLoading = - isLoadingPatients || - addPatientMutation.isPending || - updatePatientMutation.isPending; + const isLoading = addPatientMutation.isPending; // Search handling const handleSearch = (criteria: SearchCriteria) => { @@ -258,42 +121,6 @@ export default function PatientsPage() { setSearchCriteria(null); }; - // Filter patients based on search criteria - const filteredPatients = useMemo(() => { - if (!searchCriteria || !searchCriteria.searchTerm) { - return patients; - } - - const term = searchCriteria.searchTerm.toLowerCase(); - return patients.filter((patient) => { - switch (searchCriteria.searchBy) { - case "name": - return ( - patient.firstName.toLowerCase().includes(term) || - patient.lastName.toLowerCase().includes(term) - ); - case "phone": - return patient.phone.toLowerCase().includes(term); - case "insuranceProvider": - return patient.insuranceProvider?.toLowerCase().includes(term); - case "insuranceId": - return patient.insuranceId?.toLowerCase().includes(term); - case "all": - default: - return ( - patient.firstName.toLowerCase().includes(term) || - patient.lastName.toLowerCase().includes(term) || - patient.phone.toLowerCase().includes(term) || - patient.email?.toLowerCase().includes(term) || - patient.address?.toLowerCase().includes(term) || - patient.city?.toLowerCase().includes(term) || - patient.insuranceProvider?.toLowerCase().includes(term) || - patient.insuranceId?.toLowerCase().includes(term) - ); - } - }); - }, [patients, searchCriteria]); - // File upload handling const handleFileUpload = (file: File) => { setIsUploading(true); @@ -373,16 +200,6 @@ export default function PatientsPage() { New Patient -
@@ -441,213 +258,20 @@ export default function PatientsPage() { isSearchActive={!!searchCriteria} /> - {searchCriteria && ( -
-

- Found {filteredPatients.length} - {filteredPatients.length === 1 ? " patient" : " patients"} - {searchCriteria.searchBy !== "all" - ? ` with ${searchCriteria.searchBy}` - : ""} - matching "{searchCriteria.searchTerm}" -

-
- )} - - - setIsDeletePatientOpen(false)} - entityName={currentPatient?.name} + allowDelete={true} + allowEdit={true} + allowView={true} /> - {/* View Patient Modal */} - - - - Patient Details - - Complete information about the patient. - - - - {currentPatient && ( -
-
-
- {currentPatient.firstName.charAt(0)} - {currentPatient.lastName.charAt(0)} -
-
-

- {currentPatient.firstName} {currentPatient.lastName} -

-

- Patient ID:{" "} - {currentPatient.id.toString().padStart(4, "0")} -

-
-
- -
-
-

- Personal Information -

-
-

- - Date of Birth: - {" "} - {new Date( - currentPatient.dateOfBirth - ).toLocaleDateString()} -

-

- Gender:{" "} - {currentPatient.gender.charAt(0).toUpperCase() + - currentPatient.gender.slice(1)} -

-

- Status:{" "} - - {currentPatient.status.charAt(0).toUpperCase() + - currentPatient.status.slice(1)} - -

-
-
- -
-

- Contact Information -

-
-

- Phone:{" "} - {currentPatient.phone} -

-

- Email:{" "} - {currentPatient.email || "N/A"} -

-

- Address:{" "} - {currentPatient.address ? ( - <> - {currentPatient.address} - {currentPatient.city && - `, ${currentPatient.city}`} - {currentPatient.zipCode && - ` ${currentPatient.zipCode}`} - - ) : ( - "N/A" - )} -

-
-
- -
-

Insurance

-
-

- Provider:{" "} - {currentPatient.insuranceProvider - ? currentPatient.insuranceProvider === "delta" - ? "Delta Dental" - : currentPatient.insuranceProvider === "metlife" - ? "MetLife" - : currentPatient.insuranceProvider === "cigna" - ? "Cigna" - : currentPatient.insuranceProvider === - "aetna" - ? "Aetna" - : currentPatient.insuranceProvider - : "N/A"} -

-

- ID:{" "} - {currentPatient.insuranceId || "N/A"} -

-

- Group Number:{" "} - {currentPatient.groupNumber || "N/A"} -

-

- - Policy Holder: - {" "} - {currentPatient.policyHolder || "Self"} -

-
-
- -
-

- Medical Information -

-
-

- Allergies:{" "} - {currentPatient.allergies || "None reported"} -

-

- - Medical Conditions: - {" "} - {currentPatient.medicalConditions || - "None reported"} -

-
-
-
- -
- - -
-
- )} -
-
- {/* Add/Edit Patient Modal */}