'use client' import { useState, useCallback, useEffect } from 'react' import { X, ZoomIn, ZoomOut, Download, RotateCw } from 'lucide-react' interface ReceiptViewerProps { imageUrl: string thumbnailUrl?: string isOpen: boolean onClose: () => void title?: string showDownload?: boolean } export default function ReceiptViewer({ imageUrl, thumbnailUrl, isOpen, onClose, title = 'Receipt', showDownload = true }: ReceiptViewerProps) { const [zoom, setZoom] = useState(1) const [rotation, setRotation] = useState(0) const [position, setPosition] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) const [imageLoaded, setImageLoaded] = useState(false) const [imageSize, setImageSize] = useState({ width: 0, height: 0 }) // Reset state when modal opens/closes useEffect(() => { if (isOpen) { setZoom(1) setRotation(0) setPosition({ x: 0, y: 0 }) setImageLoaded(false) } }, [isOpen]) // Handle image load const handleImageLoad = useCallback((e: React.SyntheticEvent) => { const img = e.target as HTMLImageElement setImageSize({ width: img.naturalWidth, height: img.naturalHeight }) setImageLoaded(true) }, []) // Handle zoom const handleZoomIn = useCallback(() => { setZoom(prev => Math.min(prev + 0.2, 3)) }, []) const handleZoomOut = useCallback(() => { setZoom(prev => Math.max(prev - 0.2, 0.5)) }, []) const handleResetZoom = useCallback(() => { setZoom(1) setRotation(0) setPosition({ x: 0, y: 0 }) }, []) // Handle rotation const handleRotate = useCallback(() => { setRotation(prev => (prev + 90) % 360) }, []) // Handle download const handleDownload = useCallback(() => { const link = document.createElement('a') link.href = imageUrl link.download = `receipt_${Date.now()}.jpg` link.target = '_blank' document.body.appendChild(link) link.click() document.body.removeChild(link) }, [imageUrl]) // Handle mouse drag for panning const handleMouseDown = useCallback((e: React.MouseEvent) => { if (zoom > 1) { setIsDragging(true) setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y }) } }, [zoom, position]) const handleMouseMove = useCallback((e: React.MouseEvent) => { if (isDragging) { setPosition({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y }) } }, [isDragging, dragStart]) const handleMouseUp = useCallback(() => { setIsDragging(false) }, []) // Handle touch events for mobile const handleTouchStart = useCallback((e: React.TouchEvent) => { if (e.touches.length === 1 && zoom > 1) { setIsDragging(true) const touch = e.touches[0] setDragStart({ x: touch.clientX - position.x, y: touch.clientY - position.y }) } }, [zoom, position]) const handleTouchMove = useCallback((e: React.TouchEvent) => { if (isDragging && e.touches.length === 1) { const touch = e.touches[0] setPosition({ x: touch.clientX - dragStart.x, y: touch.clientY - dragStart.y }) } }, [isDragging, dragStart]) const handleTouchEnd = useCallback(() => { setIsDragging(false) }, []) // Handle keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (!isOpen) return switch (e.key) { case 'Escape': onClose() break case '+': case '=': handleZoomIn() break case '-': case '_': handleZoomOut() break case 'r': case 'R': handleRotate() break case '0': handleResetZoom() break } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [isOpen, onClose, handleZoomIn, handleZoomOut, handleRotate, handleResetZoom]) if (!isOpen) return null return (
{/* Header */}

{title}

{/* Download button */} {showDownload && ( )} {/* Zoom controls */}
{Math.round(zoom * 100)}%
{/* Rotation control */} {/* Reset button */} {/* Close button */}
{/* Image container */}
{imageLoaded && (
{imageSize.width} × {imageSize.height}px
)}
1 ? (isDragging ? 'grabbing' : 'grab') : 'default' }} > Receipt
{/* Loading indicator */} {!imageLoaded && (
)}
{/* Footer with instructions */}
Drag to pan • Scroll to zoom • Keyboard shortcuts: + - R 0 Esc
) }