'use client' import { useState, useCallback, useEffect } from 'react' import { DOCUMENT_CATEGORIES, type DocumentCategory } from '@/app/(admin)/ai/ocr-pipeline/types' const KLAUSUR_API = '/klausur-api' interface StepUploadProps { sessionId: string | null onUploaded: (sessionId: string, name: string) => void onNext: () => void } export function StepUpload({ sessionId, onUploaded, onNext }: StepUploadProps) { const [dragging, setDragging] = useState(false) const [uploading, setUploading] = useState(false) const [selectedFile, setSelectedFile] = useState(null) const [preview, setPreview] = useState(null) const [title, setTitle] = useState('') const [category, setCategory] = useState('vokabelseite') const [error, setError] = useState('') // Clean up preview URL on unmount useEffect(() => { return () => { if (preview) URL.revokeObjectURL(preview) } }, [preview]) const handleFileSelect = useCallback((file: File) => { setSelectedFile(file) setError('') if (file.type.startsWith('image/')) { setPreview(URL.createObjectURL(file)) } else { setPreview(null) } // Auto-fill title from filename if empty if (!title.trim()) { setTitle(file.name.replace(/\.[^.]+$/, '')) } }, [title]) const handleUpload = useCallback(async () => { if (!selectedFile) return setUploading(true) setError('') try { const formData = new FormData() formData.append('file', selectedFile) if (title.trim()) formData.append('name', title.trim()) const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions`, { method: 'POST', body: formData, }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.detail || `Upload fehlgeschlagen (${res.status})`) } const data = await res.json() const sid = data.session_id || data.id // Set category if (category) { await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sid}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ document_category: category }), }) } onUploaded(sid, title.trim() || selectedFile.name) } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { setUploading(false) } }, [selectedFile, title, category, onUploaded]) const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault() setDragging(false) const file = e.dataTransfer.files[0] if (file) handleFileSelect(file) }, [handleFileSelect]) const handleInputChange = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) handleFileSelect(file) }, [handleFileSelect]) const clearFile = useCallback(() => { setSelectedFile(null) if (preview) URL.revokeObjectURL(preview) setPreview(null) }, [preview]) // ---- Phase 2: Uploaded → show result + "Weiter" ---- if (sessionId) { return (
Dokument hochgeladen
{/* eslint-disable-next-line @next/next/no-img-element */} Hochgeladenes Dokument { (e.target as HTMLImageElement).style.display = 'none' }} />
{title || 'Dokument'}
Kategorie: {DOCUMENT_CATEGORIES.find(c => c.value === category)?.label || category}
Session: {sessionId.slice(0, 8)}...
) } // ---- Phase 1b: File selected → preview + "Hochladen" ---- if (selectedFile) { return (
{/* Title input */}
setTitle(e.target.value)} placeholder="z.B. Vokabeln Unit 3" className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-sm" />
{/* Category selector */}
{DOCUMENT_CATEGORIES.map(cat => ( ))}
{/* File preview */}
{preview ? (
{/* eslint-disable-next-line @next/next/no-img-element */} Vorschau
) : (
📄
)}
{selectedFile.name}
{(selectedFile.size / 1024 / 1024).toFixed(1)} MB
{error && (
{error}
)}
) } // ---- Phase 1a: No file → drop zone ---- return (
{/* Title input */}
setTitle(e.target.value)} placeholder="z.B. Vokabeln Unit 3" className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-sm" />
{/* Category selector */}
{DOCUMENT_CATEGORIES.map(cat => ( ))}
{/* Drop zone */}
{ e.preventDefault(); setDragging(true) }} onDragLeave={() => setDragging(false)} onDrop={handleDrop} className={`border-2 border-dashed rounded-xl p-12 text-center transition-colors ${ dragging ? 'border-teal-400 bg-teal-50 dark:bg-teal-900/20' : 'border-gray-300 dark:border-gray-600 hover:border-gray-400' }`} >
📤
Bild oder PDF hierher ziehen
{error && (
{error}
)}
) }