feat: Add Analysis page, run comparison, notifications, and config editor

Dashboard enhancements:
- Add Analysis page with tabs: Overview, Parameters, Pareto, Correlations, Constraints, Surrogate, Runs
- Add PlotlyCorrelationHeatmap for parameter-objective correlation analysis
- Add PlotlyFeasibilityChart for constraint satisfaction visualization
- Add PlotlySurrogateQuality for FEA vs NN prediction comparison
- Add PlotlyRunComparison for comparing optimization runs within a study

Real-time improvements:
- Replace watchdog file-watching with SQLite database polling for better Windows reliability
- Add DatabasePoller class with 2-second polling interval
- Enhanced WebSocket messages: trial_completed, new_best, pareto_update, progress

Desktop notifications:
- Add useNotifications hook using Web Notifications API
- Add NotificationSettings toggle component
- Notify users when new best solutions are found

Config editor:
- Add PUT /studies/{study_id}/config endpoint with auto-backup
- Add ConfigEditor modal with tabs: General, Variables, Objectives, Settings, JSON
- Prevents editing while optimization is running

Enhanced Pareto visualization:
- Add dark mode styling with transparent backgrounds
- Add stats bar showing Pareto, FEA, NN, and infeasible counts
- Add Pareto front connecting line for 2D view
- Add table showing top 10 Pareto-optimal solutions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Antoine
2025-12-05 19:57:20 -05:00
parent 5c660ff270
commit 5fb94fdf01
27 changed files with 5878 additions and 722 deletions

View File

@@ -1,20 +1,25 @@
import { NavLink, useNavigate } from 'react-router-dom';
import {
Home,
Settings,
Activity,
FileText,
BarChart3,
TrendingUp,
ChevronLeft,
Play,
Pause,
CheckCircle,
Clock,
Zap
Zap,
Terminal
} from 'lucide-react';
import clsx from 'clsx';
import { useStudy } from '../../context/StudyContext';
import { useClaudeTerminal } from '../../context/ClaudeTerminalContext';
export const Sidebar = () => {
const { selectedStudy, clearStudy } = useStudy();
const { isConnected: claudeConnected, setIsOpen: setClaudeTerminalOpen } = useClaudeTerminal();
const navigate = useNavigate();
const handleBackToHome = () => {
@@ -26,8 +31,12 @@ export const Sidebar = () => {
switch (status) {
case 'running':
return <Play className="w-3 h-3 text-green-400" />;
case 'paused':
return <Pause className="w-3 h-3 text-orange-400" />;
case 'completed':
return <CheckCircle className="w-3 h-3 text-blue-400" />;
case 'not_started':
return <Clock className="w-3 h-3 text-dark-400" />;
default:
return <Clock className="w-3 h-3 text-dark-400" />;
}
@@ -37,8 +46,12 @@ export const Sidebar = () => {
switch (status) {
case 'running':
return 'text-green-400';
case 'paused':
return 'text-orange-400';
case 'completed':
return 'text-blue-400';
case 'not_started':
return 'text-dark-400';
default:
return 'text-dark-400';
}
@@ -47,9 +60,10 @@ export const Sidebar = () => {
// Navigation items depend on whether a study is selected
const navItems = selectedStudy
? [
{ to: '/setup', icon: Settings, label: 'Setup' },
{ to: '/dashboard', icon: Activity, label: 'Live Tracker' },
{ to: '/results', icon: FileText, label: 'Reports' },
{ to: '/analytics', icon: BarChart3, label: 'Analytics' },
{ to: '/analysis', icon: TrendingUp, label: 'Analysis' },
{ to: '/results', icon: FileText, label: 'Results' },
]
: [
{ to: '/', icon: Home, label: 'Select Study' },
@@ -133,6 +147,23 @@ export const Sidebar = () => {
Optimization Running
</div>
)}
{selectedStudy && selectedStudy.status === 'paused' && (
<div className="flex items-center gap-2 text-sm text-orange-400 mt-1">
<div className="w-2 h-2 bg-orange-500 rounded-full" />
Optimization Paused
</div>
)}
{/* Claude Terminal Status */}
<button
onClick={() => setClaudeTerminalOpen(true)}
className={clsx(
'flex items-center gap-2 text-sm mt-1 w-full text-left hover:opacity-80 transition-opacity',
claudeConnected ? 'text-green-400' : 'text-dark-400'
)}
>
<Terminal className="w-3 h-3" />
{claudeConnected ? 'Claude Connected' : 'Claude Disconnected'}
</button>
</div>
</div>
</aside>