68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { FileText } from 'lucide-react';
|
|
import type { LabCaseAttachmentMeta } from '@/types/cases';
|
|
|
|
interface LabCaseAttachmentPreviewProps {
|
|
caseId: string;
|
|
attachment: LabCaseAttachmentMeta;
|
|
loadBlob: (caseId: string, attachmentId: string) => Promise<Blob>;
|
|
className?: string;
|
|
}
|
|
|
|
export function LabCaseAttachmentPreview({
|
|
caseId,
|
|
attachment,
|
|
loadBlob,
|
|
className = 'aspect-square w-full max-w-[11rem]',
|
|
}: LabCaseAttachmentPreviewProps) {
|
|
const [url, setUrl] = useState<string | null>(null);
|
|
const [failed, setFailed] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
let objectUrl: string | null = null;
|
|
|
|
void (async () => {
|
|
try {
|
|
const blob = await loadBlob(caseId, attachment.id);
|
|
if (cancelled) return;
|
|
objectUrl = URL.createObjectURL(blob);
|
|
setUrl(objectUrl);
|
|
setFailed(false);
|
|
} catch {
|
|
if (!cancelled) setFailed(true);
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
};
|
|
}, [caseId, attachment.id, loadBlob]);
|
|
|
|
const isImage = attachment.mimeType.startsWith('image/');
|
|
const isPdf = attachment.mimeType === 'application/pdf';
|
|
|
|
return (
|
|
<div
|
|
className={`${className} rounded-[var(--radius-md)] border border-border/60 bg-background-secondary overflow-hidden`}
|
|
title={attachment.fileName}
|
|
>
|
|
{url && isImage ? (
|
|
<img src={url} alt={attachment.fileName} className="h-full w-full object-cover" />
|
|
) : url && isPdf ? (
|
|
<iframe src={url} title={attachment.fileName} className="h-full w-full border-0" />
|
|
) : (
|
|
<div className="flex h-full w-full flex-col items-center justify-center gap-1.5 p-2 text-text-muted">
|
|
<FileText className="h-8 w-8 shrink-0 icon-flat" aria-hidden />
|
|
<span className="line-clamp-2 text-center text-[10px] leading-tight">
|
|
{failed ? 'Preview unavailable' : attachment.fileName}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|