'use client' import { useState, useEffect, useCallback } from 'react' import { useTheme } from '@/lib/ThemeContext' interface UploadedFile { id: string sessionId: string name: string type: string size: number uploadedAt: string dataUrl: string } interface QRCodeUploadProps { sessionId?: string onClose?: () => void onFileUploaded?: (file: UploadedFile) => void onFilesChanged?: (files: UploadedFile[]) => void className?: string } export function QRCodeUpload({ sessionId, onClose, onFileUploaded, onFilesChanged, className = '' }: QRCodeUploadProps) { const { isDark } = useTheme() const [qrCodeUrl, setQrCodeUrl] = useState(null) const [uploadUrl, setUploadUrl] = useState('') const [isLoading, setIsLoading] = useState(true) const [uploadedFiles, setUploadedFiles] = useState([]) const [isPolling, setIsPolling] = useState(false) // Format file size const formatFileSize = (bytes: number): string => { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] } // Fetch uploads for this session const fetchUploads = useCallback(async () => { if (!sessionId) return try { const response = await fetch(`/api/uploads?sessionId=${sessionId}`) if (response.ok) { const data = await response.json() const newFiles = data.uploads || [] // Check if there are new files if (newFiles.length > uploadedFiles.length) { const newlyAdded = newFiles.slice(uploadedFiles.length) newlyAdded.forEach((file: UploadedFile) => { if (onFileUploaded) { onFileUploaded(file) } }) } setUploadedFiles(newFiles) if (onFilesChanged) { onFilesChanged(newFiles) } } } catch (error) { console.error('Failed to fetch uploads:', error) } }, [sessionId, uploadedFiles.length, onFileUploaded, onFilesChanged]) // Initialize QR code and start polling useEffect(() => { // Generate Upload-URL let baseUrl = typeof window !== 'undefined' ? window.location.origin : '' // Hostname to IP mapping for local network const hostnameToIP: Record = { 'macmini': '192.168.178.100', 'macmini.local': '192.168.178.100', } // Replace known hostnames with IP addresses Object.entries(hostnameToIP).forEach(([hostname, ip]) => { if (baseUrl.includes(hostname)) { baseUrl = baseUrl.replace(hostname, ip) } }) const uploadPath = `/upload/${sessionId || 'new'}` const fullUrl = `${baseUrl}${uploadPath}` setUploadUrl(fullUrl) // Generate QR code via external API const qrApiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(fullUrl)}` setQrCodeUrl(qrApiUrl) setIsLoading(false) // Initial fetch fetchUploads() // Start polling for new uploads every 3 seconds setIsPolling(true) const pollInterval = setInterval(() => { fetchUploads() }, 3000) return () => { clearInterval(pollInterval) setIsPolling(false) } }, [sessionId]) // Note: fetchUploads is intentionally not in deps to avoid re-creating interval // Separate effect for fetching when uploadedFiles changes useEffect(() => { // This is just for the callback effect, actual polling is in the other useEffect }, [uploadedFiles, onFilesChanged]) const copyToClipboard = async () => { try { await navigator.clipboard.writeText(uploadUrl) alert('Link kopiert!') } catch (err) { console.error('Kopieren fehlgeschlagen:', err) } } const deleteUpload = async (id: string) => { try { const response = await fetch(`/api/uploads?id=${id}`, { method: 'DELETE' }) if (response.ok) { const newFiles = uploadedFiles.filter(f => f.id !== id) setUploadedFiles(newFiles) if (onFilesChanged) { onFilesChanged(newFiles) } } } catch (error) { console.error('Failed to delete upload:', error) } } return (
{/* Header */}
📱

Mit Mobiltelefon hochladen

QR-Code scannen oder Link teilen

{onClose && ( )}
{/* QR Code */}
{isLoading ? (
) : qrCodeUrl ? ( QR Code zum Hochladen ) : (
QR-Code nicht verfuegbar
)}

Scannen Sie diesen Code mit Ihrem Handy,
um Dokumente direkt hochzuladen.

{/* Polling indicator */} {isPolling && (
Warte auf Uploads...
)}
{/* Uploaded Files List */} {uploadedFiles.length > 0 && (

{uploadedFiles.length} Datei{uploadedFiles.length !== 1 ? 'en' : ''} empfangen

{uploadedFiles.map((file) => (
{file.type.startsWith('image/') ? '🖼️' : '📄'}

{file.name}

{formatFileSize(file.size)}

))}
)} {/* Link teilen */}

Oder Link teilen:

{/* Network hint - only show if no files uploaded yet */} {uploadedFiles.length === 0 && (
⚠️

Nur im lokalen Netzwerk

Ihr Mobiltelefon muss mit dem gleichen Netzwerk verbunden sein.

)} {/* Tip */}
💡

Tipp: Mehrere Seiten scannen

Sie koennen beliebig viele Fotos hochladen.

) } // Export the UploadedFile type for use in other components export type { UploadedFile }