import React, { useEffect, useMemo, useState, useRef } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { Delete, Edit, Eye, FileCheck, Scan, Upload, X, FileText, Download, Trash2, Camera, RefreshCw } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useMutation, useQuery } from "@tanstack/react-query"; import LoadingScreen from "@/components/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 "@/components/ui/deleteDialog"; import { useAuth } from "@/hooks/use-auth"; import { PatientSearch, SearchCriteria } from "./patient-search"; import { useDebounce } from "use-debounce"; import { cn } from "@/lib/utils"; import { Checkbox } from "@/components/ui/checkbox"; import { formatDateToHumanReadable } from "@/utils/dateUtils"; import { Patient, UpdatePatient } from "@repo/db/types"; import { PatientFinancialsModal } from "./patient-financial-modal"; import { getPageNumbers } from "@/utils/pageNumberGenerator"; import { uploadDocument, getPatientDocuments, deleteDocument, viewDocument, downloadDocument, scanDocument, formatFileSize, type PatientDocument } from "@/lib/api/documents"; interface PatientApiResponse { patients: Patient[]; totalCount: number; } interface PatientTableProps { allowEdit?: boolean; allowView?: boolean; allowDelete?: boolean; allowCheckbox?: boolean; allowNewClaim?: boolean; allowFinancial?: boolean; onNewClaim?: (patientId: number) => void; onSelectPatient?: (patient: Patient | null) => void; onPageChange?: (page: number) => void; onSearchChange?: (searchTerm: string) => void; } // 🔑 exported base key export const QK_PATIENTS_BASE = ["patients"] as const; // helper (optional) – mirrors your current key structure export const qkPatients = (page: number, search: string) => [...QK_PATIENTS_BASE, { page, search }] as const; export function PatientTable({ allowEdit, allowView, allowDelete, allowCheckbox, allowNewClaim, allowFinancial, onNewClaim, onSelectPatient, onPageChange, onSearchChange, }: PatientTableProps) { const { toast } = useToast(); const { user } = useAuth(); const isAdmin = user?.username === "admin"; const [currentPatient, setCurrentPatient] = useState( undefined ); const [isAddPatientOpen, setIsAddPatientOpen] = useState(false); const [isViewPatientOpen, setIsViewPatientOpen] = useState(false); const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false); const [isFinancialsOpen, setIsFinancialsOpen] = useState(false); const [modalPatientId, setModalPatientId] = useState(null); const [currentPage, setCurrentPage] = useState(1); const patientsPerPage = 5; const offset = (currentPage - 1) * patientsPerPage; const [isSearchActive, setIsSearchActive] = useState(false); const [searchCriteria, setSearchCriteria] = useState( null ); const [debouncedSearchCriteria] = useDebounce(searchCriteria, 500); const [selectedPatientId, setSelectedPatientId] = useState( null ); const [expandedPatientId, setExpandedPatientId] = useState(null); // Real document data state const [patientDocuments, setPatientDocuments] = useState<{ [key: number]: PatientDocument[] }>({}); const [documentsLoading, setDocumentsLoading] = useState<{ [key: number]: boolean }>({}); // Document viewer modal state const [documentViewerOpen, setDocumentViewerOpen] = useState(false); const [viewingDocument, setViewingDocument] = useState<{ url: string; name: string } | null>(null); // Thumbnail cache for image documents const [documentThumbnails, setDocumentThumbnails] = useState<{ [key: number]: string | null }>({}); // Camera modal state const [cameraOpen, setCameraOpen] = useState(false); const [selectedPatientIdForCamera, setSelectedPatientIdForCamera] = useState(null); const [stream, setStream] = useState(null); const [capturedImage, setCapturedImage] = useState(null); const [cameraPermission, setCameraPermission] = useState<'granted' | 'denied' | 'prompt' | 'unknown'>('unknown'); const [isRequestingPermission, setIsRequestingPermission] = useState(false); const videoRef = useRef(null); const canvasRef = useRef(null); const [facingMode, setFacingMode] = useState<'user' | 'environment'>('environment'); // Cleanup blob URL when modal closes const handleDocumentViewerClose = () => { if (viewingDocument && viewingDocument.url.startsWith('blob:')) { URL.revokeObjectURL(viewingDocument.url); } setDocumentViewerOpen(false); setViewingDocument(null); }; // Get thumbnail URL for image documents const getDocumentThumbnail = async (doc: PatientDocument): Promise => { if (!doc.mimeType.startsWith('image/')) { return null; } try { // Use the full URL from the database directly const documentUrl = doc.filePath; // Static files don't need authentication headers const response = await fetch(documentUrl); if (!response.ok) { return null; } const blob = await response.blob(); return URL.createObjectURL(blob); } catch (error) { console.error('Error loading thumbnail:', error); return null; } }; // Load thumbnails for image documents const loadDocumentThumbnails = async (documents: PatientDocument[]) => { const imageDocs = documents.filter(doc => doc.mimeType.startsWith('image/')); for (const doc of imageDocs) { if (!documentThumbnails[doc.id]) { const thumbnailUrl = await getDocumentThumbnail(doc); setDocumentThumbnails(prev => ({ ...prev, [doc.id]: thumbnailUrl })); } } }; // Check camera permissions const checkCameraPermission = async () => { try { console.log('Checking camera permission...'); const permission = await navigator.permissions.query({ name: 'camera' as PermissionName }); console.log('Permission state:', permission.state); setCameraPermission(permission.state as 'granted' | 'denied' | 'prompt'); // Listen for permission changes permission.addEventListener('change', () => { console.log('Permission changed to:', permission.state); setCameraPermission(permission.state as 'granted' | 'denied' | 'prompt'); }); return permission.state; } catch (error) { console.log('Permission API not supported, will try getUserMedia directly'); return 'unknown'; } }; // Check if running on localhost vs remote IP const isLocalhost = () => { return window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' || window.location.hostname.includes('localhost'); }; // Check if any camera devices are available const checkCameraDevices = async () => { try { const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter(device => device.kind === 'videoinput'); console.log('Available camera devices:', videoDevices.length); console.log('Current hostname:', window.location.hostname); console.log('Is localhost:', isLocalhost()); return videoDevices.length > 0; } catch (error) { console.error('Error checking camera devices:', error); return false; } }; // Request camera permission const requestCameraPermission = async () => { setIsRequestingPermission(true); // Check if running on secure context if (!isLocalhost() && window.location.protocol !== 'https:') { toast({ title: "HTTPS Required", description: "Camera access requires HTTPS when accessing from remote devices. Please use https:// or access from localhost.", variant: "destructive", }); setIsRequestingPermission(false); return false; } // First check if any camera devices are available const hasCamera = await checkCameraDevices(); if (!hasCamera) { toast({ title: "No Camera Found", description: "No camera device was detected. Please ensure you have a camera connected and try again.", variant: "destructive", }); setIsRequestingPermission(false); return false; } try { console.log('Requesting camera permission...'); const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', width: { ideal: 1920 }, height: { ideal: 1080 } } }); console.log('Camera permission granted!'); // Permission granted setCameraPermission('granted'); setStream(mediaStream); if (videoRef.current) { videoRef.current.srcObject = mediaStream; } // Stop the stream for now, will be started when needed mediaStream.getTracks().forEach(track => track.stop()); setStream(null); toast({ title: "Camera Permission Granted", description: "You can now use the camera to scan documents", }); return true; } catch (error: any) { console.error('Camera permission error:', error); // Handle different types of camera errors if (error.name === 'NotFoundError' || error.message.includes('Requested device not found')) { setCameraPermission('denied'); toast({ title: "No Camera Found", description: "No camera device was detected. Please ensure you have a camera connected and try again.", variant: "destructive", }); } else if (error.name === 'NotAllowedError' || error.message.includes('Permission denied')) { setCameraPermission('denied'); toast({ title: "Camera Permission Denied", description: "Please allow camera access in your browser settings to scan documents", variant: "destructive", }); } else if (error.name === 'NotReadableError' || error.message.includes('Already in use')) { setCameraPermission('denied'); toast({ title: "Camera Already in Use", description: "The camera is being used by another application. Please close other apps using the camera and try again.", variant: "destructive", }); } else if (error.name === 'NotSupportedError' || error.message.includes('secure context')) { setCameraPermission('denied'); toast({ title: "HTTPS Required", description: "Camera access requires HTTPS. Please use https:// or access from localhost for development.", variant: "destructive", }); } else { setCameraPermission('denied'); toast({ title: "Camera Error", description: "Unable to access camera. Please check your camera settings and try again.", variant: "destructive", }); } return false; } finally { setIsRequestingPermission(false); } }; // Camera functions const startCamera = async () => { try { const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingMode, width: { ideal: 1920 }, height: { ideal: 1080 } } }); setStream(mediaStream); if (videoRef.current) { videoRef.current.srcObject = mediaStream; } } catch (error) { console.error('Error accessing camera:', error); toast({ title: "Camera Error", description: "Unable to access camera. Please check permissions.", variant: "destructive", }); } }; const stopCamera = () => { if (stream) { stream.getTracks().forEach(track => track.stop()); setStream(null); } }; const capturePhoto = () => { if (videoRef.current && canvasRef.current) { const video = videoRef.current; const canvas = canvasRef.current; const context = canvas.getContext('2d'); if (context) { canvas.width = video.videoWidth; canvas.height = video.videoHeight; context.drawImage(video, 0, 0, canvas.width, canvas.height); const imageData = canvas.toDataURL('image/jpeg', 0.8); setCapturedImage(imageData); stopCamera(); } } }; const retakePhoto = () => { setCapturedImage(null); startCamera(); }; const toggleFacingMode = () => { setFacingMode(prev => prev === 'user' ? 'environment' : 'user'); if (stream) { stopCamera(); } }; // Restart camera when facingMode changes useEffect(() => { if (cameraOpen && !capturedImage) { startCamera(); } }, [facingMode]); const handleFinancialsModalChange = (v: boolean) => { setIsFinancialsOpen(v); if (!v) setModalPatientId(null); }; const handleScanDocument = async (patientId: number) => { console.log('handleScanDocument called with patientId:', patientId); if (!patientId) { console.error('No patient ID provided'); toast({ title: "Error", description: "Please select a patient first", variant: "destructive", }); return; } setSelectedPatientIdForCamera(patientId); setCapturedImage(null); setFacingMode('environment'); // Reset to back camera by default // Directly request permission which triggers browser prompt const granted = await requestCameraPermission(); if (granted) { setCameraOpen(true); } }; const handleCameraClose = () => { stopCamera(); setCameraOpen(false); setSelectedPatientIdForCamera(null); setCapturedImage(null); }; const saveCapturedPhoto = async () => { if (!capturedImage || !selectedPatientIdForCamera) return; try { // Convert data URL to blob const response = await fetch(capturedImage); const blob = await response.blob(); const file = new File([blob], `scan_${Date.now()}.jpg`, { type: 'image/jpeg' }); // Upload using existing upload function await uploadDocument(selectedPatientIdForCamera, file); // Emit document upload event window.dispatchEvent(new CustomEvent('documentUploaded', { detail: { patientId: selectedPatientIdForCamera, fileName: `scan_${Date.now()}.jpg` } })); // Also set storage event for cross-tab communication localStorage.setItem('documentUploaded', Date.now().toString()); // Refresh documents for this patient await loadPatientDocuments(selectedPatientIdForCamera); toast({ title: "Success", description: "Document scanned and uploaded successfully", variant: "default", }); handleCameraClose(); } catch (error) { console.error('Error saving scanned document:', error); toast({ title: "Error", description: "Failed to save scanned document", variant: "destructive", }); } }; // Start camera when modal opens useEffect(() => { if (cameraOpen && !capturedImage) { startCamera(); } return () => { if (stream) { stopCamera(); } }; }, [cameraOpen, capturedImage]); const handleSelectPatient = (patient: Patient) => { const isSelected = selectedPatientId === patient.id; const newSelectedId = isSelected ? null : patient.id; setSelectedPatientId(newSelectedId ? Number(newSelectedId) : null); if (onSelectPatient) { onSelectPatient(isSelected ? null : patient); } }; const handleClearSelection = () => { setSelectedPatientId(null); if (onSelectPatient) { onSelectPatient(null); } }; const handleUploadFiles = async () => { if (selectedPatientId) { const input = document.createElement('input'); input.type = 'file'; input.multiple = true; input.accept = '.pdf,.jpg,.jpeg,.png,.doc,.docx'; input.onchange = async (e) => { const files = (e.target as HTMLInputElement).files; if (files && files.length > 0) { try { const uploadPromises = Array.from(files).map(file => uploadDocument(selectedPatientId, file) ); const results = await Promise.all(uploadPromises); // Emit document upload event window.dispatchEvent(new CustomEvent('documentUploaded', { detail: { patientId: selectedPatientId, fileCount: files.length, files: Array.from(files).map(f => f.name) } })); // Also set storage event for cross-tab communication localStorage.setItem('documentUploaded', Date.now().toString()); // Refresh documents for this patient await loadPatientDocuments(selectedPatientId); toast({ title: "Files Uploaded", description: `${files.length} file(s) uploaded successfully to patient ID: ${selectedPatientId}`, variant: "default", }); } catch (error) { console.error('Upload error:', error); toast({ title: "Upload Failed", description: error instanceof Error ? error.message : "Failed to upload files", variant: "destructive", }); } } }; input.click(); } }; // Load documents for a patient const loadPatientDocuments = async (patientId: number) => { try { setDocumentsLoading(prev => ({ ...prev, [patientId]: true })); const response = await getPatientDocuments(patientId); setPatientDocuments(prev => ({ ...prev, [patientId]: response.documents })); // Load thumbnails for image documents await loadDocumentThumbnails(response.documents); } catch (error) { console.error('Error loading documents:', error); toast({ title: "Error", description: "Failed to load documents", variant: "destructive", }); } finally { setDocumentsLoading(prev => ({ ...prev, [patientId]: false })); } }; const handleToggleDocuments = async (patientId: number) => { const isExpanding = expandedPatientId !== patientId; setExpandedPatientId(isExpanding ? patientId : null); // Load documents if expanding and not already loaded if (isExpanding && !patientDocuments[patientId]) { await loadPatientDocuments(patientId); } }; const handleViewDocument = async (documentId: number, patientId: number) => { try { const doc = patientDocuments[patientId]?.find(doc => doc.id === documentId); if (!doc) { console.error('Document not found'); return; } // Use the full URL from the database directly const documentUrl = doc.filePath; // For images, fetch and convert to blob URL for modal viewing if (doc.mimeType.startsWith('image/')) { // Static files don't need authentication headers const response = await fetch(documentUrl); if (!response.ok) { throw new Error('Failed to fetch document'); } const blob = await response.blob(); const url = URL.createObjectURL(blob); setViewingDocument({ url, name: doc.originalName }); setDocumentViewerOpen(true); } else { // For PDFs and other files, open in new tab const link = document.createElement('a'); link.href = documentUrl; link.target = '_blank'; link.rel = 'noopener noreferrer'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } } catch (error) { console.error('Error viewing document:', error); toast({ title: "Error", description: "Failed to view document. Please try again.", variant: "destructive", }); } }; const handleDownloadDocument = async (documentId: number, patientId: number) => { try { const doc = patientDocuments[patientId]?.find(doc => doc.id === documentId); if (!doc) { toast({ title: "Error", description: "Document not found", variant: "destructive", }); return; } // Use the full URL from the database directly const documentUrl = doc.filePath; // For better download experience, fetch the file and create blob URL try { const response = await fetch(documentUrl); if (!response.ok) { throw new Error('Failed to fetch document for download'); } const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); // Create a temporary link and trigger download const link = document.createElement('a'); link.href = blobUrl; link.download = doc.originalName; document.body.appendChild(link); link.click(); document.body.removeChild(link); // Clean up blob URL URL.revokeObjectURL(blobUrl); } catch (fetchError) { // Fallback to direct link if fetch fails const link = document.createElement('a'); link.href = documentUrl; link.download = doc.originalName; document.body.appendChild(link); link.click(); document.body.removeChild(link); } toast({ title: "Success", description: "Document downloaded successfully", }); } catch (error) { console.error('Error downloading document:', error); toast({ title: "Error", description: "Failed to download document. Please try again.", variant: "destructive", }); } }; const handleDeleteDocument = async (documentId: number, patientId: number) => { try { await deleteDocument(documentId); // Refresh documents for this patient await loadPatientDocuments(patientId); toast({ title: "Document Deleted", description: "Document has been deleted successfully", variant: "default", }); } catch (error) { console.error('Error deleting document:', error); toast({ title: "Error", description: error instanceof Error ? error.message : "Failed to delete document", variant: "destructive", }); } }; const searchKeyPart = debouncedSearchCriteria?.searchTerm || "recent"; const { data: patientsData, isLoading, isError, } = useQuery({ queryKey: qkPatients(currentPage, searchKeyPart), queryFn: async () => { const trimmedTerm = debouncedSearchCriteria?.searchTerm?.trim(); const isSearch = trimmedTerm && trimmedTerm.length > 0; const rawSearchBy = debouncedSearchCriteria?.searchBy || "name"; const validSearchKeys = [ "name", "phone", "insuranceId", "gender", "dob", "all", ]; const searchKey = validSearchKeys.includes(rawSearchBy) ? rawSearchBy : "name"; let url: string; if (isSearch) { const searchParams = new URLSearchParams({ limit: String(patientsPerPage), offset: String(offset), }); if (searchKey === "all") { searchParams.set("term", trimmedTerm!); } else { searchParams.set(searchKey, trimmedTerm!); } url = `/api/patients/search?${searchParams.toString()}`; } else { url = `/api/patients/recent?limit=${patientsPerPage}&offset=${offset}`; } const res = await apiRequest("GET", url); if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.message || "Search failed"); } 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: async () => { setIsAddPatientOpen(false); // this query every page, await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); 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: async () => { setIsDeletePatientOpen(false); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); 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: Number(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(Number(currentPatient.id)); } else { toast({ title: "Error", description: "No patient selected for deletion.", variant: "destructive", }); } }; useEffect(() => { if (onPageChange) onPageChange(currentPage); }, [currentPage, onPageChange]); useEffect(() => { const term = debouncedSearchCriteria?.searchTerm?.trim() || "recent"; if (onSearchChange) onSearchChange(term); }, [debouncedSearchCriteria, onSearchChange]); 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", ]; return colorClasses[id % colorClasses.length]; }; return (
{/* Selection Panel */} {!!selectedPatientId && (
1 patient selected
)}
{ setSearchCriteria(criteria); setCurrentPage(1); // reset page on new search setIsSearchActive(true); }} onClearSearch={() => { setSearchCriteria({ searchTerm: "", searchBy: "name" }); // triggers `recent` setCurrentPage(1); setIsSearchActive(false); }} isSearchActive={isSearchActive} /> {allowCheckbox && Select} Patient DOB / Gender {/*Contact*/} Insurance Status Actions {isLoading ? ( ) : isError ? ( Error loading patients. ) : patientsData?.patients.length === 0 ? ( No patients found. ) : ( patientsData?.patients.map((patient) => ( {allowCheckbox && ( handleSelectPatient(patient)} /> )}
{getInitials(patient.firstName, patient.lastName)}
{patient.firstName} {patient.lastName}
PID-{patient.id?.toString().padStart(4, "0")}
{formatDateToHumanReadable(patient.dateOfBirth)}
{patient.gender}
{/*
{patient.phone}
{patient.email}
*/}
{patient.insuranceProvider ?? "Not specified"}
{patient.insuranceId && (
ID: {patient.insuranceId}
)}
{patient.status === "ACTIVE" && ( Active )} {patient.status === "INACTIVE" && ( Inactive )} {patient.status === "UNKNOWN" && ( Unknown )} {patient.status === "PLAN_NOT_ACCEPTED" && ( Plan Not Accepted )}
{allowDelete && isAdmin && ( )} {allowFinancial && ( )} {allowEdit && ( )} {allowNewClaim && ( )} {allowView && ( )}
)) )}
{/* 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:{" "} {formatDateToHumanReadable(currentPatient.dateOfBirth)}

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

Status:{" "} {currentPatient.status === "PLAN_NOT_ACCEPTED" ? "Plan Not Accepted" : currentPatient.status ? currentPatient.status.charAt(0).toUpperCase() + currentPatient.status.slice(1).toLowerCase() : "Unknown"}

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 */} {/* Financial Modal */} { setIsFinancialsOpen(v); if (!v) setModalPatientId(null); }} /> setIsDeletePatientOpen(false)} entityName={currentPatient?.firstName} /> {/* 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" : "" } /> {getPageNumbers(currentPage, totalPages).map((page, idx) => ( {page === "..." ? ( ... ) : ( { e.preventDefault(); setCurrentPage(page as number); }} isActive={currentPage === page} > {page} )} ))} { e.preventDefault(); if (currentPage < totalPages) setCurrentPage(currentPage + 1); }} className={ currentPage === totalPages ? "pointer-events-none opacity-50" : "" } />
)} {/* Document Viewer Modal */}
Document Viewer Viewing: {viewingDocument?.name}
{viewingDocument && ( <> {viewingDocument.name.toLowerCase().endsWith('.pdf') ? (