import { useState, useEffect } from 'react' import { createPortal } from 'react-dom' import { ExternalLink, Download, X, FileText, ChevronLeft, ChevronRight, FileImage } from 'lucide-react' import { useTranslation } from '../../i18n' import { getAuthUrl } from '../../api/authUrl' import { openFile as openFileUrl } from '../../utils/fileDownload' import { triggerDownload, isImage } from '../Files/FileManager.helpers' import type { BudgetItemReceipt } from '../../types' interface ReceiptPreviewModalProps { receipts: BudgetItemReceipt[] initialIndex?: number onClose: () => void } export function ReceiptPreviewModal({ receipts, initialIndex = 0, onClose }: ReceiptPreviewModalProps) { const { t } = useTranslation() const [index, setIndex] = useState(Math.max(0, Math.min(initialIndex, receipts.length - 1))) const [signedUrl, setSignedUrl] = useState('') const [loading, setLoading] = useState(false) const current = receipts[index] const isImg = isImage(current?.mime_type) const isPdf = current?.mime_type === 'application/pdf' || current?.original_name?.toLowerCase().endsWith('.pdf') useEffect(() => { if (!current) return let cancelled = false setLoading(true) setSignedUrl('') getAuthUrl(current.url, 'download') .then(url => { if (!cancelled) { setSignedUrl(url) setLoading(false) } }) .catch(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [current?.url]) useEffect(() => { const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { // The preview opens on top of the expense form, and both the desktop // Modal and the phone MSheet close themselves on Escape from a listener // on `document`. Without this the one keypress closed the form too and // threw away whatever the user had typed into it. e.preventDefault() e.stopPropagation() onClose() return } if (e.key === 'ArrowLeft' && index > 0) setIndex(i => i - 1) if (e.key === 'ArrowRight' && index < receipts.length - 1) setIndex(i => i + 1) } // Capture phase, so this runs before the listeners the parents put on // `document`; stopPropagation on the bubble phase would be too late. window.addEventListener('keydown', handleKey, true) return () => window.removeEventListener('keydown', handleKey, true) }, [index, receipts.length, onClose]) if (!current) return null const handleOpenTab = () => { openFileUrl(current.url, current.original_name).catch(() => {}) } const handleDownload = () => { triggerDownload(current.url, current.original_name) } return createPortal(
{current.original_name}