demo / frontend /src /components /BenchmarkCreateForm.jsx
tfrere's picture
first commit
970eef1
raw
history blame
8.23 kB
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;