'use client'; // ADR-021: FilePreviewModal — แสดงไฟล์แนบ Workflow Step โดยไม่บังคับดาวน์โหลด (US4) // รองรับ: PDF (iframe), Image (img), อื่นๆ (ลิงก์ดาวน์โหลด) // Auth: ดึงไฟล์ผ่าน apiClient เพื่อแนบ JWT header อัตโนมัติ → แปลงเป็น BlobURL import { useEffect, useState } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Loader2, Download, FileIcon } from 'lucide-react'; import type { AxiosError } from 'axios'; import apiClient from '@/lib/api/client'; import { useTranslations } from '@/hooks/use-translations'; import type { WorkflowAttachmentSummary } from '@/types/workflow'; interface FilePreviewModalProps { attachment: WorkflowAttachmentSummary | null; onClose: () => void; // ADR-021 T041: เรียกเมื่อ API คืน 404 (ไฟล์ถูกลบออกจาก Storage) onUnavailable?: (publicId: string) => void; } // แปลง bytes เป็น KB/MB สำหรับแสดงขนาดไฟล์ function formatBytes(bytes?: number): string { if (!bytes) return ''; if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } // ตรวจสอบว่า mimeType เป็น PDF หรือ Image function getPreviewType(mimeType?: string): 'pdf' | 'image' | 'none' { if (!mimeType) return 'none'; if (mimeType === 'application/pdf') return 'pdf'; if (mimeType.startsWith('image/')) return 'image'; return 'none'; } export function FilePreviewModal({ attachment, onClose, onUnavailable }: FilePreviewModalProps) { const t = useTranslations(); const [blobUrl, setBlobUrl] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // ดึงไฟล์จาก API เมื่อ attachment เปลี่ยน — แปลงเป็น BlobURL เพื่อรองรับ JWT auth useEffect(() => { if (!attachment) { setBlobUrl(null); return; } let currentUrl: string | null = null; setIsLoading(true); setError(null); apiClient .get(`/files/preview/${attachment.publicId}`, { responseType: 'blob' }) .then((res) => { const url = URL.createObjectURL(res.data as Blob); currentUrl = url; setBlobUrl(url); }) .catch((err: AxiosError) => { // ADR-021 T041: ตรวจสอบ 404 เพื่อแยกแยะกรณี ไฟล์ถูกลบ vs เกิดผิดพลาดอื่น if (err.response?.status === 404) { setError(t('filePreview.fileUnavailable')); if (attachment?.publicId) onUnavailable?.(attachment.publicId); } else { setError(t('filePreview.loadError')); } }) .finally(() => { setIsLoading(false); }); // Cleanup: เพิกถอน BlobURL เพื่อป้องกัน memory leak return () => { if (currentUrl) URL.revokeObjectURL(currentUrl); }; }, [attachment, onUnavailable, t]); const previewType = getPreviewType(attachment?.mimeType); return ( !open && onClose()}> {/* Header — ชื่อไฟล์ + ขนาด */} {attachment?.originalFilename ?? t('filePreview.fallbackTitle')} {attachment?.fileSize && ( {formatBytes(attachment.fileSize)} )} {/* Body — Preview Area */}
{isLoading && (
)} {error && !isLoading && (
{error ?? t('filePreview.loadError')}
)} {!isLoading && !error && blobUrl && previewType === 'pdf' && (