diff --git a/src/components/cases/documents-tab.tsx b/src/components/cases/documents-tab.tsx index b25823b..544ec93 100644 --- a/src/components/cases/documents-tab.tsx +++ b/src/components/cases/documents-tab.tsx @@ -14,9 +14,17 @@ import { FolderPlus, ChevronRight, Home, + Eye, + ExternalLink, } from "lucide-react"; import { formatDate } from "@/lib/format"; import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; const MAX_BYTES = 50 * 1024 * 1024; @@ -29,6 +37,9 @@ export function CaseDocumentsTab({ caseId }: { caseId: string }) { const [dragOver, setDragOver] = useState(false); const [creatingFolder, setCreatingFolder] = useState(false); const [newFolderName, setNewFolderName] = useState(""); + const [previewDoc, setPreviewDoc] = useState(null); + const [previewUrl, setPreviewUrl] = useState(null); + const [previewLoading, setPreviewLoading] = useState(false); const fileRef = useRef(null); const load = useCallback(async () => { @@ -205,6 +216,23 @@ export function CaseDocumentsTab({ caseId }: { caseId: string }) { window.open(data.signedUrl, "_blank"); }; + const openPreview = async (doc: any) => { + setPreviewDoc(doc); + setPreviewUrl(null); + setPreviewLoading(true); + const { data, error } = await supabase.storage + .from("case-documents") + .createSignedUrl(doc.storage_path, 300); + setPreviewLoading(false); + if (error) { toast.error(error.message); setPreviewDoc(null); return; } + setPreviewUrl(data.signedUrl); + }; + + const closePreview = () => { + setPreviewDoc(null); + setPreviewUrl(null); + }; + const del = async (doc: any) => { if (!confirm(`Delete ${doc.name}?`)) return; await supabase.storage.from("case-documents").remove([doc.storage_path]); @@ -341,8 +369,9 @@ export function CaseDocumentsTab({ caseId }: { caseId: string }) { {d.size_bytes ? `${(d.size_bytes / 1024).toFixed(1)} KB` : "—"} - - + + + ))} @@ -351,6 +380,80 @@ export function CaseDocumentsTab({ caseId }: { caseId: string }) { + + !o && closePreview()}> + + + {previewDoc?.name} + {previewUrl && ( +
+ + +
+ )} +
+
+ {previewLoading ? ( +
+ Loading preview… +
+ ) : previewUrl && previewDoc ? ( + + ) : null} +
+
+
+ + ); +} + +function PreviewBody({ url, doc }: { url: string; doc: any }) { + const mime = (doc.mime_type || "").toLowerCase(); + const name = (doc.name || "").toLowerCase(); + const isImage = mime.startsWith("image/") || /\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(name); + const isPdf = mime === "application/pdf" || name.endsWith(".pdf"); + const isText = + mime.startsWith("text/") || + /\.(txt|md|csv|json|log|xml|yml|yaml)$/i.test(name); + const isAudio = mime.startsWith("audio/") || /\.(mp3|wav|m4a|ogg)$/i.test(name); + const isVideo = mime.startsWith("video/") || /\.(mp4|webm|mov)$/i.test(name); + + if (isImage) { + return ( +
+ {doc.name} +
+ ); + } + if (isPdf || isText) { + return