import React, { useEffect, useState } from "react"; import { apiRequest } from "@/lib/queryClient"; import { toast } from "@/hooks/use-toast"; import { Maximize2, Minimize2, Download, X } from "lucide-react"; import { viewDocument } from "@/lib/api/documents"; type Props = { fileId: number | null; isOpen: boolean; onClose: () => void; initialFileName?: string | null; isPatientDocument?: boolean; directImageUrl?: string; // Add prop for direct image URL }; export default function DocumentsFilePreviewModal({ fileId, isOpen, onClose, initialFileName, isPatientDocument = false, directImageUrl, }: Props) { const [loading, setLoading] = useState(false); const [mime, setMime] = useState(null); const [fileName, setFileName] = useState( initialFileName ?? null ); const [blobUrl, setBlobUrl] = useState(null); const [error, setError] = useState(null); const [isFullscreen, setIsFullscreen] = useState(false); useEffect(() => { if (!isOpen || !fileId) return; let cancelled = false; let createdUrl: string | null = null; async function load() { setLoading(true); setError(null); setMime(null); setFileName(initialFileName ?? null); setBlobUrl(null); try { let res: Response; if (directImageUrl) { // Use direct image URL without API call setBlobUrl(directImageUrl); // Try to determine MIME type from file extension const extension = directImageUrl.split('.').pop()?.toLowerCase(); if (extension) { if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(extension)) { setMime(`image/${extension === 'jpg' ? 'jpeg' : extension}`); } else if (extension === 'pdf') { setMime('application/pdf'); } } setLoading(false); return; } else if (isPatientDocument && fileId) { // For patient documents, use the viewDocument API to get the URL const documentUrl = viewDocument(fileId); res = await fetch(documentUrl); } else { // For PDF files, use the existing endpoint res = await apiRequest( "GET", `/api/documents/pdf-files/${fileId}` ); } if (!res.ok) { // try to parse error message from JSON body let msg = `Preview request failed (${res.status})`; try { const j = await res.json(); msg = j?.message ?? msg; } catch { } throw new Error(msg); } // try to infer MIME from headers; fallback to application/pdf const contentType = res.headers.get("content-type") ?? "application/pdf"; setMime(contentType); // If server provided filename in headers (Content-Disposition), we could parse it here. // Use initialFileName if provided, otherwise keep unset until download. if (!fileName && initialFileName) setFileName(initialFileName); const arrayBuffer = await res.arrayBuffer(); if (cancelled) return; const blob = new Blob([arrayBuffer], { type: contentType }); createdUrl = URL.createObjectURL(blob); setBlobUrl(createdUrl); } catch (err: any) { if (!cancelled) setError(err?.message ?? String(err)); } finally { if (!cancelled) setLoading(false); } } load(); return () => { cancelled = true; if (createdUrl) URL.revokeObjectURL(createdUrl); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen, fileId]); useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } if (isOpen) { window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); } }, [isOpen, onClose]); if (!isOpen) return null; async function handleDownload() { if (!fileId) return; try { let downloadUrl: string; if (directImageUrl) { // Use the direct image URL and fetch as blob to force download const response = await fetch(directImageUrl); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const link = window.document.createElement("a"); link.href = url; link.download = fileName ?? `file-${fileId}`; window.document.body.appendChild(link); link.click(); window.document.body.removeChild(link); window.URL.revokeObjectURL(url); return; } else { // For PDF files, use the existing endpoint const res = await apiRequest("GET", `/api/documents/pdf-files/${fileId}`); if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error(j?.message || `Download failed (${res.status})`); } const arrayBuffer = await res.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: mime ?? "application/octet-stream", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileName ?? `file-${fileId}`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 5000); return; } // For download API URLs, create download link const a = document.createElement("a"); a.href = downloadUrl; a.download = fileName ?? `file-${fileId}`; document.body.appendChild(a); a.click(); a.remove(); } catch (err: any) { toast({ title: "Download failed", description: err?.message ?? String(err), variant: "destructive", }); } } const containerBase = "bg-white rounded-md p-3 flex flex-col overflow-hidden shadow-xl"; const sizeClass = isFullscreen ? "w-[95vw] h-[95vh]" : "w-[min(1200px,95vw)] h-[85vh]"; return (

{fileName ?? `File #${fileId}`}

{mime ?? ""}
{loading && (
Loading preview…
)} {error &&
{error}
} {!loading && !error && blobUrl && mime?.startsWith("image/") && (
{fileName
)} {!loading && !error && blobUrl && (mime === "application/pdf" || mime?.endsWith("/pdf")) && (