Spaces:
Sleeping
Sleeping
Download frontend/src/components/BatchProcessor.jsx from habib-analyst/email-agent-api: direct link, hf CLI and curl.
- Browser
- Download file 3.78 kB
-
https://huggingface.co/spaces/habib-analyst/email-agent-api/resolve/main/frontend/src/components/BatchProcessor.jsx
- Command line
-
hf download hf://spaces/habib-analyst/email-agent-api/frontend/src/components/BatchProcessor.jsx
-
curl -L -o BatchProcessor.jsx https://huggingface.co/spaces/habib-analyst/email-agent-api/resolve/main/frontend/src/components/BatchProcessor.jsx
3.78 kB
| import { useState, useEffect, useCallback, useRef } from "react"; | |
| import { batchAPI } from "../api/client"; | |
| import RecipientInput from "./RecipientInput"; | |
| import EmailQueue from "./EmailQueue"; | |
| import toast from "react-hot-toast"; | |
| export default function BatchProcessor({ currentSubject, currentHtml }) { | |
| const [batchQueue, setBatchQueue] = useState([]); | |
| const [batchStats, setBatchStats] = useState({}); | |
| const [batchMode, setBatchMode] = useState("manual"); | |
| const [batchCurrentIdx, setBatchCurrentIdx] = useState(-1); | |
| const [autoRunning, setAutoRunning] = useState(false); | |
| const [loading, setLoading] = useState(false); | |
| const pollRef = useRef(null); | |
| const refreshBatchStatus = useCallback(async () => { | |
| try { | |
| const res = await batchAPI.getQueue(); | |
| setBatchQueue(res.data.queue); | |
| setBatchStats(res.data.stats); | |
| setBatchMode(res.data.mode); | |
| setBatchCurrentIdx(res.data.current_index); | |
| return res.data; | |
| } catch (err) { | |
| console.error("Failed to fetch batch queue", err); | |
| } | |
| }, []); | |
| useEffect(() => { | |
| refreshBatchStatus(); | |
| const interval = setInterval(refreshBatchStatus, 3000); | |
| return () => clearInterval(interval); | |
| }, [refreshBatchStatus]); | |
| const handleBatchStart = async (profList, mode, delayMin, delayMax) => { | |
| setLoading(true); | |
| try { | |
| await batchAPI.start(profList, mode, delayMin, delayMax); | |
| toast.success("Batch started!"); | |
| refreshBatchStatus(); | |
| } catch (err) { | |
| toast.error(err.response?.data?.detail || "Failed to start batch"); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleBatchApprove = async (index) => { | |
| try { | |
| await batchAPI.send(index); | |
| toast.success("Email sent!"); | |
| refreshBatchStatus(); | |
| } catch (err) { | |
| toast.error("Send failed"); | |
| } | |
| }; | |
| const handleBatchSkip = async (index) => { | |
| try { | |
| await batchAPI.skip(index); | |
| toast.success("Skipped"); | |
| refreshBatchStatus(); | |
| } catch { | |
| toast.error("Skip failed"); | |
| } | |
| }; | |
| const handleAutoRun = async () => { | |
| setAutoRunning(true); | |
| try { | |
| await batchAPI.autoRun(); | |
| } catch (err) { | |
| if (err.name !== "CanceledError") { | |
| toast.error("Auto-run stopped or failed"); | |
| } | |
| } finally { | |
| setAutoRunning(false); | |
| refreshBatchStatus(); | |
| } | |
| }; | |
| const handleStopAuto = async () => { | |
| try { | |
| await batchAPI.stop(); | |
| setAutoRunning(false); | |
| toast.success("Stopped"); | |
| } catch { | |
| toast.error("Failed to stop"); | |
| } | |
| }; | |
| return ( | |
| <div className="space-y-6"> | |
| <RecipientInput | |
| onBatchStart={handleBatchStart} | |
| loading={loading} | |
| /> | |
| {batchQueue.length > 0 && ( | |
| <div className="animate-slide-up"> | |
| <EmailQueue | |
| queue={batchQueue} | |
| currentIndex={batchCurrentIdx} | |
| stats={batchStats} | |
| batchMode={batchMode} | |
| autoRunning={autoRunning} | |
| onSelect={(idx) => setBatchCurrentIdx(idx)} | |
| onApprove={handleBatchApprove} | |
| onSkip={handleBatchSkip} | |
| onAutoRun={handleAutoRun} | |
| onStopAuto={handleStopAuto} | |
| /> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |