import React, { useState } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useToast } from "@/hooks/use-toast"; import { Checkbox } from "@/components/ui/checkbox"; import { ArrowLeft, Upload as UploadIcon, Database, Tag, Eye, EyeOff, ExternalLink, CheckCircle, AlertCircle, Loader2, } from "lucide-react"; import { useApi } from "@/contexts/ApiContext"; interface DatasetInfo { dataset_repo_id: string; single_task: string; num_episodes: number; saved_episodes?: number; session_elapsed_seconds?: number; fps?: number; total_frames?: number; robot_type?: string; } interface UploadConfig { tags: string[]; private: boolean; } const Upload = () => { const location = useLocation(); const navigate = useNavigate(); const { toast } = useToast(); const { baseUrl, fetchWithHeaders } = useApi(); // Get initial dataset info from navigation state const initialDatasetInfo = location.state?.datasetInfo as DatasetInfo; // State for actual dataset info (will be loaded from backend) const [datasetInfo, setDatasetInfo] = useState(null); const [isLoadingDatasetInfo, setIsLoadingDatasetInfo] = useState(true); // Upload configuration state const [uploadConfig, setUploadConfig] = useState({ tags: ["robotics", "lerobot"], private: false, }); const [tagsInput, setTagsInput] = useState(uploadConfig.tags.join(", ")); const [isUploading, setIsUploading] = useState(false); const [uploadSuccess, setUploadSuccess] = useState(false); // Load actual dataset information from backend React.useEffect(() => { const loadDatasetInfo = async () => { if (!initialDatasetInfo?.dataset_repo_id) { toast({ title: "No Dataset Information", description: "Please complete a recording session first.", variant: "destructive", }); navigate("/"); return; } try { const response = await fetchWithHeaders(`${baseUrl}/dataset-info`, { method: "POST", body: JSON.stringify({ dataset_repo_id: initialDatasetInfo.dataset_repo_id, }), }); const data = await response.json(); if (response.ok && data.success) { // Merge the loaded dataset info with any session info we have setDatasetInfo({ ...data, saved_episodes: data.num_episodes, // Use actual episodes from dataset session_elapsed_seconds: initialDatasetInfo.session_elapsed_seconds || 0, }); } else { // Fallback to initial dataset info if backend fails toast({ title: "Warning", description: "Could not load complete dataset information. Using session data.", variant: "destructive", }); setDatasetInfo(initialDatasetInfo); } } catch (error) { console.error("Error loading dataset info:", error); // Fallback to initial dataset info toast({ title: "Warning", description: "Could not connect to backend. Using session data.", variant: "destructive", }); setDatasetInfo(initialDatasetInfo); } finally { setIsLoadingDatasetInfo(false); } }; loadDatasetInfo(); }, [initialDatasetInfo, navigate, toast]); const formatDuration = (seconds: number): string => { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; if (hours > 0) { return `${hours}h ${minutes}m ${secs}s`; } else if (minutes > 0) { return `${minutes}m ${secs}s`; } else { return `${secs}s`; } }; const handleUploadToHub = async () => { if (!datasetInfo) return; setIsUploading(true); try { // Parse tags from input const tags = tagsInput .split(",") .map((tag) => tag.trim()) .filter((tag) => tag.length > 0); const response = await fetchWithHeaders(`${baseUrl}/upload-dataset`, { method: "POST", body: JSON.stringify({ dataset_repo_id: datasetInfo.dataset_repo_id, tags, private: uploadConfig.private, }), }); const data = await response.json(); if (response.ok && data.success) { setUploadSuccess(true); toast({ title: "Upload Successful!", description: `Dataset ${datasetInfo.dataset_repo_id} has been uploaded to HuggingFace Hub.`, }); } else { toast({ title: "Upload Failed", description: data.message || "Failed to upload dataset to HuggingFace Hub.", variant: "destructive", }); } } catch (error) { console.error("Error uploading dataset:", error); toast({ title: "Connection Error", description: "Could not connect to the backend server.", variant: "destructive", }); } finally { setIsUploading(false); } }; const handleSkipUpload = () => { toast({ title: "Upload Skipped", description: "Dataset saved locally. You can upload it manually later.", }); navigate("/"); }; // Show loading state while fetching dataset info if (isLoadingDatasetInfo || !datasetInfo) { return (

Loading dataset information...

); } return (
{/* Header */}
{uploadSuccess ? ( ) : ( )}

{uploadSuccess ? "Upload Complete" : "Dataset Upload"}

{/* Success State */} {uploadSuccess && (

Successfully Uploaded!

Your dataset has been uploaded to HuggingFace Hub and is now available for training and sharing.

)} {/* Upload Form */} {!uploadSuccess && ( <> {/* Dataset Summary */}

Dataset Summary

Repository ID:

{datasetInfo.dataset_repo_id}

Task:

{datasetInfo.single_task}

Episodes Recorded:

{datasetInfo.saved_episodes || datasetInfo.num_episodes}

{datasetInfo.total_frames && (

{datasetInfo.total_frames} total frames

)}
Session Duration:

{formatDuration(datasetInfo.session_elapsed_seconds || 0)}

{datasetInfo.fps && (

{datasetInfo.fps} FPS

)}
{/* Upload Configuration */}

Upload Configuration

{/* Tags */}
setTagsInput(e.target.value)} placeholder="robotics, lerobot, manipulation" className="bg-gray-800 border-gray-600 text-white" />

Tags help others discover your dataset on HuggingFace Hub

{/* Privacy Setting */}
setUploadConfig({ ...uploadConfig, private: checked as boolean, }) } />
{uploadConfig.private ? ( ) : ( )}

{uploadConfig.private ? "Only you will be able to access this dataset" : "Dataset will be publicly accessible on HuggingFace Hub"}

{/* Action Buttons */}
{/* Info Box */}

About HuggingFace Hub Upload

  • • Your dataset will be uploaded to HuggingFace Hub for sharing and collaboration
  • • You need to be logged in to HuggingFace CLI on the server
  • • Uploaded datasets can be used for training models and sharing with the community
  • • You can always upload manually later using the HuggingFace CLI
)}
); }; export default Upload;