diff --git a/fe-admin/src/components/pe/AttachmentPreviewDialog.tsx b/fe-admin/src/components/pe/AttachmentPreviewDialog.tsx index 85c9a01..38d45e7 100644 --- a/fe-admin/src/components/pe/AttachmentPreviewDialog.tsx +++ b/fe-admin/src/components/pe/AttachmentPreviewDialog.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { Loader2, AlertTriangle } from 'lucide-react' +import { Loader2, AlertTriangle, Maximize2, Minimize2 } from 'lucide-react' import { Dialog } from '@/components/ui/Dialog' import { Button } from '@/components/ui/Button' import { api } from '@/lib/api' @@ -24,17 +24,33 @@ type Props = { /** Preview file inline qua BE endpoint `/view` (Content-Disposition: inline). * Fetch as blob → object URL → iframe (PDF) hoặc img (image). * Bearer auth qua axios api client (KHÔNG thể set iframe src trực tiếp vì - * iframe không inherit Authorization header). */ + * iframe không inherit Authorization header). + * [C7 anh Kiệt FDC] Nút "Toàn màn hình" → lớp phủ inset-0 phóng to preview hết + * cỡ viewport (Dialog dùng chung chỉ có sm/md/lg → tự render overlay riêng). */ export function AttachmentPreviewDialog({ open, evaluationId, attachmentId, fileName, onClose, }: Props) { const [blobUrl, setBlobUrl] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) + const [fullscreen, setFullscreen] = useState(false) const ext = fileName.toLowerCase().split('.').pop() ?? '' const isImage = ['png', 'jpg', 'jpeg', 'webp', 'gif'].includes(ext) + // Reset toàn-màn-hình mỗi khi đóng / đổi file. + useEffect(() => { if (!open) setFullscreen(false) }, [open]) + + // Esc khi đang toàn-màn-hình → thoát toàn-màn-hình TRƯỚC (không đóng luôn Dialog). + useEffect(() => { + if (!fullscreen) return + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { e.stopPropagation(); setFullscreen(false) } + } + window.addEventListener('keydown', onKey, true) + return () => window.removeEventListener('keydown', onKey, true) + }, [fullscreen]) + useEffect(() => { if (!open) return let cancelled = false @@ -64,34 +80,65 @@ export function AttachmentPreviewDialog({ } }, [open, evaluationId, attachmentId]) + const ready = blobUrl && !loading && !error + return ( - Đóng} - > -
- {loading && ( -
- - Đang tải file… + <> + + + + + } + > +
+ {loading && ( +
+ + Đang tải file… +
+ )} + {error && !loading && ( +
+ +
Không tải được file
+
{error}
+
+ )} + {ready && ( + isImage + ? {fileName} + :