From fb6b28d3fbba7bbbbe117856f5d81e298fdbae83 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 16 May 2025 14:28:17 +0530 Subject: [PATCH] savenschedule working and delete issue in pateint page, add patint issue on dashboard and patient page --- apps/Backend/src/routes/patients.ts | 1 + .../src/components/patients/patient-table.tsx | 16 +- .../src/components/staffs/staff-table.tsx | 31 ++- .../src/components/ui/deleteDialog.tsx | 36 +++ apps/Frontend/src/pages/dashboard.tsx | 74 +++++- apps/Frontend/src/pages/patients-page.tsx | 230 +++++++++++++++++- apps/Frontend/src/pages/settings-page.tsx | 33 ++- 7 files changed, 386 insertions(+), 35 deletions(-) create mode 100644 apps/Frontend/src/components/ui/deleteDialog.tsx diff --git a/apps/Backend/src/routes/patients.ts b/apps/Backend/src/routes/patients.ts index 5f4ff95..e77c4bf 100644 --- a/apps/Backend/src/routes/patients.ts +++ b/apps/Backend/src/routes/patients.ts @@ -200,6 +200,7 @@ router.delete( await storage.deletePatient(patientId); res.status(204).send(); } catch (error) { + console.error("Delete patient error:", error); res.status(500).json({ message: "Failed to delete patient" }); } } diff --git a/apps/Frontend/src/components/patients/patient-table.tsx b/apps/Frontend/src/components/patients/patient-table.tsx index 1599e99..29eaf5f 100644 --- a/apps/Frontend/src/components/patients/patient-table.tsx +++ b/apps/Frontend/src/components/patients/patient-table.tsx @@ -14,7 +14,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Button } from "@/components/ui/button"; -import { Edit, Eye, MoreVertical } from "lucide-react"; +import { Delete, Edit, Eye, MoreVertical } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { @@ -39,9 +39,10 @@ interface PatientTableProps { patients: Patient[]; onEdit: (patient: Patient) => void; onView: (patient: Patient) => void; + onDelete: (patient: Patient) => void; } -export function PatientTable({ patients, onEdit, onView }: PatientTableProps) { +export function PatientTable({ patients, onEdit, onView, onDelete }: PatientTableProps) { const [currentPage, setCurrentPage] = useState(1); const patientsPerPage = 5; @@ -167,6 +168,17 @@ export function PatientTable({ patients, onEdit, onView }: PatientTableProps) {
+ - + + + + ); diff --git a/apps/Frontend/src/components/ui/deleteDialog.tsx b/apps/Frontend/src/components/ui/deleteDialog.tsx new file mode 100644 index 0000000..3c3d957 --- /dev/null +++ b/apps/Frontend/src/components/ui/deleteDialog.tsx @@ -0,0 +1,36 @@ +export const DeleteConfirmationDialog = ({ + isOpen, + onConfirm, + onCancel, + patientName, +}: { + isOpen: boolean; + onConfirm: () => void; + onCancel: () => void; + patientName?: string; +}) => { + if (!isOpen) return null; + + return ( +
+
+

Confirm Deletion

+

Are you sure you want to delete {patientName}?

+
+ + +
+
+
+ ); +}; diff --git a/apps/Frontend/src/pages/dashboard.tsx b/apps/Frontend/src/pages/dashboard.tsx index 89fb84e..ca330fc 100644 --- a/apps/Frontend/src/pages/dashboard.tsx +++ b/apps/Frontend/src/pages/dashboard.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useRef} from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { format } from "date-fns"; import { TopAppBar } from "@/components/layout/top-app-bar"; @@ -33,6 +33,7 @@ import { 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; @@ -82,10 +83,17 @@ const updatePatientSchema = ( type UpdatePatient = z.infer; +// Type for the ref to access modal methods +type AddPatientModalRef = { + shouldSchedule: boolean; + navigateToSchedule: (patientId: number) => void; +}; + 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 @@ -96,6 +104,9 @@ export default function Dashboard() { const { toast } = useToast(); const { user } = useAuth(); + const addPatientModalRef = useRef(null); + + // Fetch patients const { data: patients = [], isLoading: isLoadingPatients } = useQuery< @@ -176,6 +187,31 @@ export default function Dashboard() { }, }); + + 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); }; @@ -216,6 +252,28 @@ export default function Dashboard() { 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; + // Create appointment mutation const createAppointmentMutation = useMutation({ mutationFn: async (appointment: InsertAppointment) => { @@ -491,19 +549,27 @@ export default function Dashboard() { patients={filteredPatients} onEdit={handleEditPatient} onView={handleViewPatient} + onDelete={handleDeletePatient} /> + + setIsDeletePatientOpen(false)} + patientName={currentPatient?.name} + /> +
{/* Add/Edit Patient Modal */} diff --git a/apps/Frontend/src/pages/patients-page.tsx b/apps/Frontend/src/pages/patients-page.tsx index a975941..85ebe3d 100644 --- a/apps/Frontend/src/pages/patients-page.tsx +++ b/apps/Frontend/src/pages/patients-page.tsx @@ -24,6 +24,14 @@ import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas 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"; const PatientSchema = ( PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject @@ -64,6 +72,7 @@ export default function PatientsPage() { const { user } = useAuth(); const [isAddPatientOpen, setIsAddPatientOpen] = useState(false); const [isViewPatientOpen, setIsViewPatientOpen] = useState(false); + const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false); const [currentPatient, setCurrentPatient] = useState( undefined ); @@ -152,12 +161,35 @@ export default function PatientsPage() { }, }); + 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); }; const handleAddPatient = (patient: InsertPatient) => { - // Add userId to the patient data if (user) { addPatientMutation.mutate({ ...patient, @@ -193,6 +225,23 @@ export default function PatientsPage() { 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 || @@ -370,10 +419,178 @@ export default function PatientsPage() { patients={filteredPatients} onEdit={handleEditPatient} onView={handleViewPatient} + onDelete={handleDeletePatient} + /> + + setIsDeletePatientOpen(false)} + patientName={currentPatient?.name} /> + {/* 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 */} diff --git a/apps/Frontend/src/pages/settings-page.tsx b/apps/Frontend/src/pages/settings-page.tsx index 601ffe1..e140f5b 100644 --- a/apps/Frontend/src/pages/settings-page.tsx +++ b/apps/Frontend/src/pages/settings-page.tsx @@ -9,6 +9,7 @@ import { StaffUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; import { z } from "zod"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { StaffForm } from "@/components/staffs/staff-form"; +import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog"; // Correctly infer Staff type from zod schema type Staff = z.infer; @@ -120,6 +121,7 @@ export default function SettingsPage() { return id; }, onSuccess: () => { + setIsDeleteStaffOpen(false); queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Removed", @@ -188,14 +190,28 @@ export default function SettingsPage() { } }, [isAddSuccess, isUpdateSuccess]); - // Delete staff - const handleDeleteStaff = (id: number) => { - if (confirm("Are you sure you want to delete this staff member?")) { - deleteStaffMutation.mutate(id); + const [isDeleteStaffOpen, setIsDeleteStaffOpen] = useState(false); + const [currentStaff, setCurrentStaff] = useState( + undefined + ); + + const handleDeleteStaff = (staff: Staff) => { + setCurrentStaff(staff); + setIsDeleteStaffOpen(true); + }; + + const handleConfirmDeleteStaff = async () => { + if (currentStaff?.id) { + deleteStaffMutation.mutate(currentStaff.id); + } else { + toast({ + title: "Error", + description: "No Staff selected for deletion.", + variant: "destructive", + }); } }; - // View staff handler (just an alert for now) const handleViewStaff = (staff: Staff) => alert( `Viewing staff member:\n${staff.name} (${staff.email || "No email"})` @@ -227,6 +243,13 @@ export default function SettingsPage() { {(error as Error)?.message || "Failed to load staff data."}

)} + + setIsDeleteStaffOpen(false)} + patientName={currentStaff?.name} + />