/** * FinalizeModal - Modal for finalizing an inbox study * * Allows user to: * - Select/create topic folder * - Choose whether to run baseline FEA * - See progress during finalization */ import React, { useState, useEffect } from 'react'; import { X, Folder, CheckCircle, Loader2, AlertCircle, } from 'lucide-react'; import { intakeApi } from '../../api/intake'; import { TopicInfo, InboxStudyDetail } from '../../types/intake'; interface FinalizeModalProps { studyName: string; topics: TopicInfo[]; onClose: () => void; onFinalized: (finalPath: string) => void; } export const FinalizeModal: React.FC = ({ studyName, topics, onClose, onFinalized, }) => { const [studyDetail, setStudyDetail] = useState(null); const [selectedTopic, setSelectedTopic] = useState(''); const [newTopic, setNewTopic] = useState(''); const [runBaseline, setRunBaseline] = useState(true); const [isLoading, setIsLoading] = useState(true); const [isFinalizing, setIsFinalizing] = useState(false); const [progress, setProgress] = useState(''); const [error, setError] = useState(null); // Load study detail useEffect(() => { const loadStudy = async () => { try { const detail = await intakeApi.getInboxStudy(studyName); setStudyDetail(detail); // Pre-select topic if set in spec if (detail.spec.meta.topic) { setSelectedTopic(detail.spec.meta.topic); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load study'); } finally { setIsLoading(false); } }; loadStudy(); }, [studyName]); const handleFinalize = async () => { const topic = newTopic.trim() || selectedTopic; if (!topic) { setError('Please select or create a topic folder'); return; } setIsFinalizing(true); setError(null); setProgress('Starting finalization...'); try { setProgress('Validating study configuration...'); await new Promise((r) => setTimeout(r, 500)); // Visual feedback if (runBaseline) { setProgress('Running baseline FEA solve...'); } const result = await intakeApi.finalize(studyName, { topic, run_baseline: runBaseline, }); setProgress('Finalization complete!'); await new Promise((r) => setTimeout(r, 500)); onFinalized(result.final_path); } catch (err) { setError(err instanceof Error ? err.message : 'Finalization failed'); setIsFinalizing(false); } }; return (
{/* Header */}

Finalize Study

{studyName}

{!isFinalizing && ( )}
{/* Content */}
{isLoading ? (
) : isFinalizing ? ( /* Progress View */

{progress}

Please wait while your study is being finalized...

) : ( <> {/* Error Display */} {error && (
{error}
)} {/* Study Summary */} {studyDetail && (

Study Summary

Status: {studyDetail.spec.meta.status}
Model Files: {studyDetail.files.sim.length + studyDetail.files.prt.length + studyDetail.files.fem.length}
Design Variables: {studyDetail.spec.design_variables?.length || 0}
Objectives: {studyDetail.spec.objectives?.length || 0}
)} {/* Topic Selection */}
or { setNewTopic(e.target.value.replace(/[^A-Za-z0-9_]/g, '_')); setSelectedTopic(''); }} placeholder="New_Topic" className="flex-1 px-4 py-2.5 rounded-lg bg-dark-800 border border-dark-600 text-white placeholder-dark-500 focus:border-primary-400 focus:outline-none focus:ring-1 focus:ring-primary-400/50" />

Study will be created at: studies/{newTopic || selectedTopic || ''}/{studyName}/

{/* Baseline Option */}
)}
{/* Footer */} {!isLoading && !isFinalizing && (
)}
); }; export default FinalizeModal;