import React, { useState, useRef, useCallback, useEffect } from 'react' const STATUS_POLL_MS = 1500 // poll /api/upload/status every 1.5s while running export default function UploadModal({ open, onClose, onSuccess }) { const [phase, setPhase] = useState('idle') // idle | uploading | running | done | error const [message, setMessage] = useState('') const [logs, setLogs] = useState('') const [showLogs, setShowLogs] = useState(false) const [dragOver, setDragOver] = useState(false) const [filename, setFilename] = useState('') const fileRef = useRef(null) const pollRef = useRef(null) const logsRef = useRef(null) // Auto-scroll logs useEffect(() => { if (logsRef.current) logsRef.current.scrollTop = logsRef.current.scrollHeight }, [logs]) // Stop polling on unmount useEffect(() => () => clearInterval(pollRef.current), []) // Reset when modal opens useEffect(() => { if (open) { setPhase('idle'); setMessage(''); setLogs('') setShowLogs(false); setFilename(''); setDragOver(false) // Also reset server-side job state fetch('/api/upload/reset', { method: 'POST' }).catch(() => {}) } }, [open]) function startPolling() { clearInterval(pollRef.current) pollRef.current = setInterval(async () => { try { const [sRes, lRes] = await Promise.all([ fetch('/api/upload/status'), fetch('/api/upload/logs'), ]) const s = await sRes.json() const l = await lRes.json() setLogs(l.logs || '') setMessage(s.message || '') if (s.status === 'done') { clearInterval(pollRef.current) setPhase('done') } else if (s.status === 'error') { clearInterval(pollRef.current) setPhase('error') setShowLogs(true) } } catch { // network hiccup — keep polling } }, STATUS_POLL_MS) } async function handleFile(file) { if (!file) return if (!file.name.match(/\.(xlsx|xlsm|xls)$/i)) { setPhase('error') setMessage('Only .xlsx / .xlsm / .xls files are accepted.') return } setFilename(file.name) setPhase('uploading') setMessage('Uploading file…') const form = new FormData() form.append('file', file) try { const res = await fetch('/api/upload', { method: 'POST', body: form }) const json = await res.json() if (!res.ok) { setPhase('error') setMessage(json.detail || 'Upload failed.') return } setPhase('running') setMessage('Pipeline started — processing data…') startPolling() } catch (err) { setPhase('error') setMessage('Cannot reach upload server. Is it running on port 8000?') } } const onDrop = useCallback(e => { e.preventDefault(); setDragOver(false) handleFile(e.dataTransfer.files[0]) }, []) const onDragOver = e => { e.preventDefault(); setDragOver(true) } const onDragLeave = () => setDragOver(false) function handleReload() { onClose() onSuccess(filename) // pass filename back to caller } if (!open) return null const isRunning = phase === 'uploading' || phase === 'running' return (
{logs || '(waiting for output…)'}
)}