Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
import React, { useState, useRef, useEffect } from "react"; | |
import { | |
Box, | |
Paper, | |
Typography, | |
CircularProgress, | |
Alert, | |
Button, | |
Stepper, | |
Step, | |
StepLabel, | |
} from "@mui/material"; | |
import { useLocation } from "react-router-dom"; | |
import CloudUploadIcon from "@mui/icons-material/CloudUpload"; | |
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | |
import AuthContainer from "./shared/AuthContainer"; | |
import { useThemeMode } from "../hooks/useThemeMode"; | |
import getTheme from "../config/theme"; | |
/** | |
* Component to display a stepper with three steps: Login, Upload File, and Generate | |
* | |
* @param {Object} props - Component props | |
* @param {number} props.activeStep - Current active step (0-based index) | |
* @returns {JSX.Element} Stepper component | |
*/ | |
const StepsDisplay = ({ activeStep }) => { | |
const steps = ["Login", "Upload File", "Generate"]; | |
return ( | |
<Box sx={{ width: "100%", mb: 4 }}> | |
<Stepper activeStep={activeStep} alternativeLabel> | |
{steps.map((label) => ( | |
<Step key={label}> | |
<StepLabel>{label}</StepLabel> | |
</Step> | |
))} | |
</Stepper> | |
</Box> | |
); | |
}; | |
/** | |
* Component for creating a new benchmark, including authentication, file upload, and generation initiation | |
* | |
* @param {Object} props - Component props | |
* @param {Function} props.onStartGeneration - Callback when generation starts with sessionId | |
* @returns {JSX.Element} BenchmarkCreateForm component | |
*/ | |
function BenchmarkCreateForm({ onStartGeneration }) { | |
const { mode } = useThemeMode(); | |
const theme = getTheme(mode); | |
const [isDragging, setIsDragging] = useState(false); | |
const [uploadStatus, setUploadStatus] = useState(null); | |
const [isLoading, setIsLoading] = useState(false); | |
const [activeStep, setActiveStep] = useState(0); | |
const [sessionId, setSessionId] = useState(null); | |
const fileInputRef = useRef(null); | |
const location = useLocation(); | |
// Check if we're coming back from an OAuth redirect | |
useEffect(() => { | |
// If we have code in URL parameters, it's an OAuth callback | |
const params = new URLSearchParams(window.location.search); | |
if (params.has("code")) { | |
console.log("Detected OAuth callback, cleaning URL"); | |
// Remove the query parameters from the URL without reloading | |
window.history.replaceState({}, document.title, window.location.pathname); | |
// Check if we have auth data in localStorage after a brief delay to let OAuth process complete | |
setTimeout(() => { | |
const storedAuth = localStorage.getItem("hf_oauth"); | |
if (storedAuth) { | |
console.log("Found auth data after redirect, refreshing UI state"); | |
setActiveStep(1); // Move to next step if authenticated | |
} | |
}, 1000); | |
} | |
}, [location]); | |
const handleDragOver = (e) => { | |
e.preventDefault(); | |
setIsDragging(true); | |
}; | |
const handleDragLeave = () => { | |
setIsDragging(false); | |
}; | |
const handleClick = () => { | |
fileInputRef.current.click(); | |
}; | |
const handleFileChange = (e) => { | |
const file = e.target.files[0]; | |
if (!file) return; | |
// Vérifier si c'est un PDF, TXT, HTML ou MD | |
if ( | |
!file.name.endsWith(".pdf") && | |
!file.name.endsWith(".txt") && | |
!file.name.endsWith(".html") && | |
!file.name.endsWith(".md") | |
) { | |
setUploadStatus({ | |
success: false, | |
message: "Only PDF, TXT, HTML and MD files are accepted", | |
}); | |
return; | |
} | |
handleFileUpload(file); | |
}; | |
const handleFileUpload = async (file) => { | |
setIsLoading(true); | |
setUploadStatus(null); | |
try { | |
const formData = new FormData(); | |
formData.append("file", file); | |
const response = await fetch("http://localhost:3001/upload", { | |
method: "POST", | |
body: formData, | |
}); | |
const result = await response.json(); | |
if (response.ok) { | |
setUploadStatus({ | |
success: true, | |
message: `File ${result.filename} uploaded successfully`, | |
}); | |
// Store the session ID for the benchmark generation | |
setSessionId(result.session_id); | |
setActiveStep(2); // Advance to Generate step after successful upload | |
} else { | |
setUploadStatus({ | |
success: false, | |
message: result.error || "Upload failed", | |
}); | |
} | |
} catch (error) { | |
setUploadStatus({ | |
success: false, | |
message: "Server connection error", | |
}); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
const handleDrop = async (e) => { | |
e.preventDefault(); | |
setIsDragging(false); | |
const file = e.dataTransfer.files[0]; | |
if (!file) { | |
setUploadStatus({ success: false, message: "No file detected" }); | |
return; | |
} | |
// Vérifier si c'est un PDF, TXT, HTML ou MD | |
if ( | |
!file.name.endsWith(".pdf") && | |
!file.name.endsWith(".txt") && | |
!file.name.endsWith(".html") && | |
!file.name.endsWith(".md") | |
) { | |
setUploadStatus({ | |
success: false, | |
message: "Only PDF, TXT, HTML and MD files are accepted", | |
}); | |
return; | |
} | |
handleFileUpload(file); | |
}; | |
const handleGenerateClick = () => { | |
if (onStartGeneration && sessionId) { | |
onStartGeneration(sessionId); | |
} | |
}; | |
return ( | |
<> | |
<StepsDisplay activeStep={activeStep} /> | |
{/* Authentication step */} | |
{activeStep === 0 && ( | |
<AuthContainer | |
actionText="use this demo" | |
onSuccess={() => setActiveStep(1)} | |
/> | |
)} | |
{/* File upload step */} | |
{activeStep === 1 && ( | |
<Paper | |
elevation={3} | |
sx={{ | |
p: 4, | |
mt: 3, | |
mb: 3, | |
border: isDragging | |
? `2px dashed ${theme.palette.primary.main}` | |
: "2px dashed #ccc", | |
backgroundColor: isDragging ? "rgba(0, 0, 0, 0.05)" : "transparent", | |
display: "flex", | |
flexDirection: "column", | |
alignItems: "center", | |
justifyContent: "center", | |
minHeight: 200, | |
cursor: "pointer", | |
transition: "all 0.3s ease", | |
}} | |
onDragOver={handleDragOver} | |
onDragLeave={handleDragLeave} | |
onDrop={handleDrop} | |
onClick={handleClick} | |
> | |
<input | |
type="file" | |
ref={fileInputRef} | |
onChange={handleFileChange} | |
accept=".pdf,.txt,.html,.md" | |
style={{ display: "none" }} | |
/> | |
<CloudUploadIcon | |
sx={{ fontSize: 60, color: "text.secondary", mb: 1 }} | |
/> | |
<Typography variant="h6" component="div" gutterBottom> | |
Drag and drop your file here or click to browse | |
</Typography> | |
<Typography variant="body2" color="text.secondary"> | |
Accepted formats: PDF, TXT, HTML, MD | |
</Typography> | |
{isLoading && ( | |
<Box sx={{ mt: 2 }}> | |
<CircularProgress size={30} /> | |
</Box> | |
)} | |
{uploadStatus && ( | |
<Alert | |
severity={uploadStatus.success ? "success" : "error"} | |
sx={{ mt: 2, width: "100%" }} | |
> | |
{uploadStatus.message} | |
</Alert> | |
)} | |
</Paper> | |
)} | |
{/* Generate button step */} | |
{activeStep === 2 && ( | |
<Paper | |
elevation={3} | |
sx={{ | |
p: 4, | |
mt: 3, | |
mb: 3, | |
display: "flex", | |
flexDirection: "column", | |
alignItems: "center", | |
justifyContent: "center", | |
minHeight: 200, | |
}} | |
> | |
<PlayArrowIcon | |
sx={{ fontSize: 60, color: "text.secondary", mb: 1 }} | |
/> | |
<Typography variant="h6" component="div" gutterBottom> | |
Ready to generate your benchmark | |
</Typography> | |
<Button | |
variant="contained" | |
color="primary" | |
onClick={handleGenerateClick} | |
sx={{ mt: 2 }} | |
startIcon={<PlayArrowIcon />} | |
> | |
Generate Benchmark | |
</Button> | |
</Paper> | |
)} | |
</> | |
); | |
} | |
export default BenchmarkCreateForm; | |