import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/ui/dialog"; import PortDetectionModal from "@/components/ui/PortDetectionModal"; import PortDetectionButton from "@/components/ui/PortDetectionButton"; import CameraConfiguration, { CameraConfig, } from "@/components/recording/CameraConfiguration"; import { useApi } from "@/contexts/ApiContext"; import { useAutoSave } from "@/hooks/useAutoSave"; interface RecordingModalProps { open: boolean; onOpenChange: (open: boolean) => void; leaderPort: string; setLeaderPort: (value: string) => void; followerPort: string; setFollowerPort: (value: string) => void; leaderConfig: string; setLeaderConfig: (value: string) => void; followerConfig: string; setFollowerConfig: (value: string) => void; leaderConfigs: string[]; followerConfigs: string[]; datasetRepoId: string; setDatasetRepoId: (value: string) => void; singleTask: string; setSingleTask: (value: string) => void; numEpisodes: number; setNumEpisodes: (value: number) => void; cameras: CameraConfig[]; setCameras: (cameras: CameraConfig[]) => void; isLoadingConfigs: boolean; onStart: () => void; releaseStreamsRef?: React.MutableRefObject<(() => void) | null>; } const RecordingModal: React.FC = ({ open, onOpenChange, leaderPort, setLeaderPort, followerPort, setFollowerPort, leaderConfig, setLeaderConfig, followerConfig, setFollowerConfig, leaderConfigs, followerConfigs, datasetRepoId, setDatasetRepoId, singleTask, setSingleTask, numEpisodes, setNumEpisodes, cameras, setCameras, isLoadingConfigs, onStart, releaseStreamsRef, }) => { const { baseUrl, fetchWithHeaders } = useApi(); const { debouncedSavePort, debouncedSaveConfig } = useAutoSave(); const [showPortDetection, setShowPortDetection] = useState(false); const [detectionRobotType, setDetectionRobotType] = useState< "leader" | "follower" >("leader"); const handlePortDetection = (robotType: "leader" | "follower") => { setDetectionRobotType(robotType); setShowPortDetection(true); }; const handlePortDetected = (port: string) => { if (detectionRobotType === "leader") { setLeaderPort(port); } else { setFollowerPort(port); } }; // Enhanced port change handlers that save automatically const handleLeaderPortChange = (value: string) => { setLeaderPort(value); // Auto-save with debouncing to avoid excessive API calls debouncedSavePort("leader", value); }; const handleFollowerPortChange = (value: string) => { setFollowerPort(value); // Auto-save with debouncing to avoid excessive API calls debouncedSavePort("follower", value); }; // Enhanced config change handlers that save automatically const handleLeaderConfigChange = (value: string) => { setLeaderConfig(value); // Auto-save with debouncing to avoid excessive API calls debouncedSaveConfig("leader", value); }; const handleFollowerConfigChange = (value: string) => { setFollowerConfig(value); // Auto-save with debouncing to avoid excessive API calls debouncedSaveConfig("follower", value); }; // Load saved ports and configurations on component mount useEffect(() => { const loadSavedData = async () => { try { // Load leader port const leaderResponse = await fetchWithHeaders( `${baseUrl}/robot-port/leader` ); const leaderData = await leaderResponse.json(); if (leaderData.status === "success" && leaderData.default_port) { setLeaderPort(leaderData.default_port); } // Load follower port const followerResponse = await fetchWithHeaders( `${baseUrl}/robot-port/follower` ); const followerData = await followerResponse.json(); if (followerData.status === "success" && followerData.default_port) { setFollowerPort(followerData.default_port); } // Load leader configuration const leaderConfigResponse = await fetchWithHeaders( `${baseUrl}/robot-config/leader?available_configs=${leaderConfigs.join( "," )}` ); const leaderConfigData = await leaderConfigResponse.json(); if ( leaderConfigData.status === "success" && leaderConfigData.default_config ) { setLeaderConfig(leaderConfigData.default_config); } // Load follower configuration const followerConfigResponse = await fetchWithHeaders( `${baseUrl}/robot-config/follower?available_configs=${followerConfigs.join( "," )}` ); const followerConfigData = await followerConfigResponse.json(); if ( followerConfigData.status === "success" && followerConfigData.default_config ) { setFollowerConfig(followerConfigData.default_config); } } catch (error) { console.error("Error loading saved data:", error); } }; if (open && leaderConfigs.length > 0 && followerConfigs.length > 0) { loadSavedData(); } }, [ open, setLeaderPort, setFollowerPort, setLeaderConfig, setFollowerConfig, leaderConfigs, followerConfigs, baseUrl, fetchWithHeaders, ]); return ( <>
REC
Configure Recording
Configure the robot arm settings and dataset parameters for recording.

Robot Configuration

handleLeaderPortChange(e.target.value)} placeholder="/dev/tty.usbmodem5A460816421" className="bg-gray-800 border-gray-700 text-white flex-1" /> handlePortDetection("leader")} robotType="leader" />
handleFollowerPortChange(e.target.value) } placeholder="/dev/tty.usbmodem5A460816621" className="bg-gray-800 border-gray-700 text-white flex-1" /> handlePortDetection("follower")} robotType="follower" />

Dataset Configuration

setDatasetRepoId(e.target.value)} placeholder="username/dataset_name" className="bg-gray-800 border-gray-700 text-white" />
setSingleTask(e.target.value)} placeholder="e.g., pick_and_place" className="bg-gray-800 border-gray-700 text-white" />
setNumEpisodes(parseInt(e.target.value))} className="bg-gray-800 border-gray-700 text-white" />
); }; export default RecordingModal;