Merge branch 'feature-pdfExtraction' into dev_replit_v2

This commit is contained in:
2025-05-23 17:38:20 +05:30
18 changed files with 2937 additions and 1602 deletions

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dental Management</title>
</head>

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 14c-1.65 0-3-1.35-3-3V5c0-1.65 1.35-3 3-3s3 1.35 3 3v6c0 1.65-1.35 3-3 3Z"></path>
<path d="M19 14v-4a7 7 0 0 0-14 0v4"></path>
<path d="M12 19c-5 0-8-2-9-5.5m18 0c-1 3.5-4 5.5-9 5.5Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@@ -0,0 +1,31 @@
import { useMutation } from "@tanstack/react-query";
import { useToast } from "@/hooks/use-toast";
import { apiRequest, queryClient } from "@/lib/queryClient";
export interface ExtractedData {
name: string;
memberId: string;
dob: string;
}
export default function useExtractPdfData() {
const { toast } = useToast();
return useMutation<ExtractedData, Error, File>({
mutationFn: async (pdfFile: File) => {
const formData = new FormData();
formData.append("pdf", pdfFile);
const res = await apiRequest("POST", "/api/pdfExtraction/extract", formData);
if (!res.ok) throw new Error("Failed to extract PDF");
return res.json();
},
onError: (error) => {
toast({
title: "Error",
description: `Failed to extract PDF: ${error.message}`,
variant: "destructive",
});
},
});
}

View File

@@ -16,14 +16,18 @@ export async function apiRequest(
): Promise<Response> {
const token = localStorage.getItem("token");
const isFormData = typeof FormData !== "undefined" && data instanceof FormData;
const headers: Record<string, string> = {
...(token ? { Authorization: `Bearer ${token}` } : {}),
// Only set Content-Type if not using FormData
...(isFormData ? {} : { "Content-Type": "application/json" }),
};
const res = await fetch(`${API_BASE_URL}${url}`, {
method,
// headers: data ? { "Content-Type": "application/json" } : {},
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}), // Include JWT token if available
},
body: data ? JSON.stringify(data) : undefined,
headers,
body: isFormData ? data as FormData : JSON.stringify(data),
credentials: "include",
});

View File

@@ -31,6 +31,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import useExtractPdfData from "@/hooks/use-extractPdfData";
const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
@@ -85,6 +86,9 @@ export default function PatientsPage() {
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [isExtracting, setIsExtracting] = useState(false);
const [formData, setFormData] = useState({ PatientName: "", PatientMemberId: "", PatientDob:"" });
const { mutate: extractPdf} = useExtractPdfData();
// Fetch patients
const {
@@ -240,10 +244,7 @@ export default function PatientsPage() {
}
};
const isLoading =
isLoadingPatients ||
addPatientMutation.isPending ||
updatePatientMutation.isPending;
const isLoading = isLoadingPatients || addPatientMutation.isPending || updatePatientMutation.isPending;
// Search handling
const handleSearch = (criteria: SearchCriteria) => {
@@ -254,18 +255,6 @@ export default function PatientsPage() {
setSearchCriteria(null);
};
// File upload handling
const handleFileUpload = (file: File) => {
setUploadedFile(file);
setIsUploading(false); // In a real implementation, this would be set to true during upload
toast({
title: "File Selected",
description: `${file.name} is ready for processing.`,
variant: "default",
});
};
// Filter patients based on search criteria
const filteredPatients = useMemo(() => {
if (!searchCriteria || !searchCriteria.searchTerm) {
@@ -302,6 +291,48 @@ export default function PatientsPage() {
});
}, [patients, searchCriteria]);
// 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",
});
setFormData({ PatientName: data.name || "", PatientMemberId: data.memberId || "", PatientDob: data.dob || ""});
},
});
};
return (
<div className="flex h-screen overflow-hidden bg-gray-100">
<Sidebar
@@ -369,6 +400,7 @@ export default function PatientsPage() {
<Button
className="w-full h-12 gap-2"
disabled={!uploadedFile || isExtracting}
onClick={handleExtract}
>
{isExtracting ? (
<>
@@ -378,7 +410,7 @@ export default function PatientsPage() {
) : (
<>
<FilePlus className="h-4 w-4" />
Extract Info
Extract Info And Claim
</>
)}
</Button>