Files
DentalManagementElogin/apps/Frontend/src/pages/patients-page.tsx

239 lines
7.0 KiB
TypeScript

import { useState, useRef } from "react";
import { useMutation } from "@tanstack/react-query";
import { PatientTable } from "@/components/patients/patient-table";
import { AddPatientModal } from "@/components/patients/add-patient-modal";
import { FileUploadZone } from "@/components/file-upload/file-upload-zone";
import { Button } from "@/components/ui/button";
import { Plus, RefreshCw, FilePlus } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { apiRequest, queryClient } from "@/lib/queryClient";
import { useAuth } from "@/hooks/use-auth";
import useExtractPdfData from "@/hooks/use-extractPdfData";
import { useLocation } from "wouter";
import { InsertPatient, Patient } from "@repo/db/types";
import { QK_PATIENTS_BASE } from "@/components/patients/patient-table";
// Type for the ref to access modal methods
type AddPatientModalRef = {
shouldSchedule: boolean;
shouldClaim: boolean;
navigateToSchedule: (patientId: number) => void;
navigateToClaim: (patientId: number) => void;
};
export default function PatientsPage() {
const { toast } = useToast();
const { user } = useAuth();
const [isAddPatientOpen, setIsAddPatientOpen] = useState(false);
const [currentPatient, setCurrentPatient] = useState<Patient | undefined>(
undefined
);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const addPatientModalRef = useRef<AddPatientModalRef | null>(null);
// File upload states
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [isExtracting, setIsExtracting] = useState(false);
const { mutate: extractPdf } = useExtractPdfData();
const [location, navigate] = useLocation();
// Add patient mutation
const addPatientMutation = useMutation({
mutationFn: async (patient: InsertPatient) => {
const res = await apiRequest("POST", "/api/patients/", patient);
return res.json();
},
onSuccess: (newPatient) => {
setIsAddPatientOpen(false);
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
toast({
title: "Success",
description: "Patient added successfully!",
variant: "default",
});
// ✅ Check claim first, then schedule
if (addPatientModalRef.current?.shouldClaim) {
addPatientModalRef.current.navigateToClaim(newPatient.id);
return;
}
if (addPatientModalRef.current?.shouldSchedule) {
addPatientModalRef.current.navigateToSchedule(newPatient.id);
return;
}
},
onError: (error) => {
toast({
title: "Error",
description: `Failed to add patient: ${error.message}`,
variant: "destructive",
});
},
});
const toggleMobileMenu = () => {
setIsMobileMenuOpen(!isMobileMenuOpen);
};
const handleAddPatient = (patient: InsertPatient) => {
if (user) {
addPatientMutation.mutate({
...patient,
userId: user.id,
});
}
};
const isLoading = addPatientMutation.isPending;
// File upload handling
const handleFileUpload = (file: File) => {
setIsUploading(true);
setUploadedFile(file);
toast({
title: "File Selected",
description: `${file.name} is ready for processing.`,
variant: "default",
});
setIsUploading(false);
};
// data extraction
const handleExtract = () => {
setIsExtracting(true);
if (!uploadedFile) {
return toast({
title: "Error",
description: "Please upload a PDF",
variant: "destructive",
});
}
extractPdf(uploadedFile, {
onSuccess: (data) => {
setIsExtracting(false);
toast({
title: "Success Pdf Data Extracted",
description: `Name: ${data.name}, Member ID: ${data.memberId}, DOB: ${data.dob}`,
variant: "default",
});
const params = new URLSearchParams({
name: data.name,
memberId: data.memberId,
dob: data.dob,
});
navigate(
`/claims?name=${encodeURIComponent(data.name)}&memberId=${data.memberId}&dob=${data.dob}`
);
},
});
};
return (
<div>
<div className="container mx-auto space-y-6">
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold tracking-tight">Patients</h1>
<p className="text-muted-foreground">
Manage patient records and information
</p>
</div>
<div className="flex space-x-2">
<Button
onClick={() => {
setCurrentPatient(undefined);
setIsAddPatientOpen(true);
}}
className="gap-1"
disabled={isLoading}
>
<Plus className="h-4 w-4" />
New Patient
</Button>
</div>
</div>
{/* File Upload Zone */}
<div className="space-y-8 py-8">
<Card>
<CardHeader>
<CardTitle>Upload Patient Document</CardTitle>
<CardDescription>
You can upload 1 file. Allowed types: PDF
</CardDescription>
</CardHeader>
<CardContent>
<FileUploadZone
onFileUpload={handleFileUpload}
isUploading={isUploading}
acceptedFileTypes="application/pdf"
/>
<div className="mt-4">
<Button
className="w-full h-12 gap-2"
disabled={!uploadedFile || isExtracting}
onClick={handleExtract}
>
{isExtracting ? (
<>
<RefreshCw className="h-4 w-4 animate-spin" />
Processing...
</>
) : (
<>
<FilePlus className="h-4 w-4" />
Extract Info & Claim/PreAuth
</>
)}
</Button>
</div>
</CardContent>
</Card>
</div>
{/* Patients Table */}
<Card>
<CardHeader>
<CardTitle>Patient Records</CardTitle>
<CardDescription>
View and manage all patient information
</CardDescription>
</CardHeader>
<CardContent>
<PatientTable
allowDelete={true}
allowEdit={true}
allowView={true}
/>
</CardContent>
</Card>
{/* Add/Edit Patient Modal */}
<AddPatientModal
ref={addPatientModalRef}
open={isAddPatientOpen}
onOpenChange={setIsAddPatientOpen}
onSubmit={handleAddPatient}
isLoading={isLoading}
patient={currentPatient}
/>
</div>
</div>
);
}