import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Card } from '../components/common/Card';
import { Button } from '../components/common/Button';
import {
Download,
FileText,
RefreshCw,
Sparkles,
Loader2,
AlertTriangle,
CheckCircle,
Copy,
Trophy,
TrendingUp,
FileJson,
FileSpreadsheet,
Settings,
ArrowRight,
ChevronDown,
ChevronUp,
Printer
} from 'lucide-react';
import { apiClient } from '../api/client';
import { useStudy } from '../context/StudyContext';
import { MarkdownRenderer } from '../components/MarkdownRenderer';
interface BestSolution {
best_trial: {
trial_number: number;
objective: number;
design_variables: Record;
user_attrs?: Record;
timestamp?: string;
} | null;
first_trial: {
trial_number: number;
objective: number;
design_variables: Record;
} | null;
improvements: Record;
total_trials: number;
}
export default function Results() {
const { selectedStudy, isInitialized } = useStudy();
const navigate = useNavigate();
const [reportContent, setReportContent] = useState(null);
const [loading, setLoading] = useState(false);
const [generating, setGenerating] = useState(false);
const [error, setError] = useState(null);
const [copied, setCopied] = useState(false);
const [lastGenerated, setLastGenerated] = useState(null);
const [bestSolution, setBestSolution] = useState(null);
const [showAllParams, setShowAllParams] = useState(false);
const [exporting, setExporting] = useState(null);
// Redirect if no study selected (but only after initialization completes)
useEffect(() => {
if (isInitialized && !selectedStudy) {
navigate('/');
}
}, [selectedStudy, navigate, isInitialized]);
// Load report and best solution when study changes
useEffect(() => {
if (selectedStudy) {
loadReport();
loadBestSolution();
}
}, [selectedStudy]);
// Show loading state while initializing (must be after all hooks)
if (!isInitialized) {
return (
);
}
const loadReport = async () => {
if (!selectedStudy) return;
setLoading(true);
setError(null);
try {
const data = await apiClient.getStudyReport(selectedStudy.id);
setReportContent(data.content);
if (data.generated_at) {
setLastGenerated(data.generated_at);
}
} catch {
// No report yet - show placeholder
setReportContent(null);
} finally {
setLoading(false);
}
};
const loadBestSolution = async () => {
if (!selectedStudy) return;
try {
const data = await apiClient.getBestSolution(selectedStudy.id);
setBestSolution(data);
} catch {
setBestSolution(null);
}
};
const handleGenerate = async () => {
if (!selectedStudy) return;
setGenerating(true);
setError(null);
try {
const data = await apiClient.generateReport(selectedStudy.id);
setReportContent(data.content);
if (data.generated_at) {
setLastGenerated(data.generated_at);
}
} catch (err: any) {
setError(err.message || 'Failed to generate report');
} finally {
setGenerating(false);
}
};
const handleCopy = async () => {
if (reportContent) {
await navigator.clipboard.writeText(reportContent);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
const handleDownload = () => {
if (!reportContent || !selectedStudy) return;
const blob = new Blob([reportContent], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${selectedStudy.id}_report.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const handlePrintPDF = () => {
if (!reportContent || !selectedStudy) return;
// Create a printable version of the report
const printWindow = window.open('', '_blank');
if (!printWindow) {
setError('Pop-up blocked. Please allow pop-ups to print PDF.');
return;
}
printWindow.document.write(`
${selectedStudy.name} - Optimization Report
${convertMarkdownToHTML(reportContent)}
`);
printWindow.document.close();
// Wait for content to load then print
printWindow.onload = () => {
printWindow.print();
};
};
// Simple markdown to HTML converter for print
const convertMarkdownToHTML = (md: string): string => {
return md
// Headers
.replace(/^### (.*$)/gm, '$1
')
.replace(/^## (.*$)/gm, '$1
')
.replace(/^# (.*$)/gm, '$1
')
// Bold and italic
.replace(/\*\*\*(.*?)\*\*\*/g, '$1')
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/\*(.*?)\*/g, '$1')
// Code blocks
.replace(/```(\w*)\n([\s\S]*?)```/g, '$2
')
.replace(/`([^`]+)`/g, '$1')
// Lists
.replace(/^\s*[-*]\s+(.*)$/gm, '$1')
.replace(/(.*<\/li>)\n(?!)/g, '$1\n')
.replace(/(?)()/g, '$1')
// Blockquotes
.replace(/^>\s*(.*)$/gm, '$1
')
// Horizontal rules
.replace(/^---$/gm, '
')
// Paragraphs
.replace(/\n\n/g, '
')
.replace(/^(.+)$/gm, (match) => {
if (match.startsWith('<')) return match;
return match;
});
};
const handleExport = async (format: 'csv' | 'json' | 'config') => {
if (!selectedStudy) return;
setExporting(format);
try {
const data = await apiClient.exportData(selectedStudy.id, format);
if (data.filename && data.content) {
const blob = new Blob([data.content], { type: data.content_type || 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = data.filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} else if (format === 'json' && data.trials) {
// Direct JSON response
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${selectedStudy.id}_data.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
} catch (err: any) {
setError(err.message || `Failed to export ${format}`);
} finally {
setExporting(null);
}
};
if (!selectedStudy) {
return null;
}
const paramEntries = bestSolution?.best_trial?.design_variables
? Object.entries(bestSolution.best_trial.design_variables)
: [];
const visibleParams = showAllParams ? paramEntries : paramEntries.slice(0, 6);
return (
{/* Header */}
{/* Error Message */}
{error && (
)}
{/* Best Solution Card */}
{bestSolution?.best_trial && (
Best Solution
Trial #{bestSolution.best_trial.trial_number} of {bestSolution.total_trials}
{bestSolution.improvements.objective && (
{bestSolution.improvements.objective.improvement_pct > 0 ? '+' : ''}
{bestSolution.improvements.objective.improvement_pct.toFixed(1)}%
improvement
)}
{/* Objective Value */}
Best Objective
{bestSolution.best_trial.objective.toExponential(4)}
{bestSolution.first_trial && (
Initial Value
{bestSolution.first_trial.objective.toExponential(4)}
)}
{bestSolution.improvements.objective && (
Absolute Change
{bestSolution.improvements.objective.absolute_change.toExponential(4)}
)}
{/* Design Variables */}
Optimal Design Variables
{paramEntries.length > 6 && (
)}
{visibleParams.map(([name, value]) => (
{name}
{typeof value === 'number' ? value.toFixed(4) : value}
))}
)}
{/* Export Options */}
Export Data
{/* Main Content - Report */}
Report Content
{lastGenerated && (
Last generated: {new Date(lastGenerated).toLocaleString()}
)}
{loading ? (
Loading report...
) : reportContent ? (
) : (
No Report Generated
Click "Generate Report" to create an AI-generated analysis of your optimization results.
}
onClick={handleGenerate}
disabled={generating}
>
Generate Report
)}
{/* Study Stats */}
Total Trials
{selectedStudy.progress.current}
Best Value
{selectedStudy.best_value?.toFixed(4) || 'N/A'}
Target
{selectedStudy.target?.toFixed(4) || 'N/A'}
Status
{selectedStudy.status}
);
}