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>
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import { Bell, BellOff, BellRing } from 'lucide-react';
|
|
import { useNotifications } from '../hooks/useNotifications';
|
|
|
|
interface NotificationSettingsProps {
|
|
compact?: boolean;
|
|
}
|
|
|
|
export function NotificationSettings({ compact = false }: NotificationSettingsProps) {
|
|
const { permission, requestPermission, isEnabled, setEnabled } = useNotifications();
|
|
|
|
const handleToggle = async () => {
|
|
if (permission === 'unsupported') {
|
|
return;
|
|
}
|
|
|
|
if (!isEnabled) {
|
|
// Enabling - request permission if needed
|
|
if (permission !== 'granted') {
|
|
const granted = await requestPermission();
|
|
if (granted) {
|
|
setEnabled(true);
|
|
}
|
|
} else {
|
|
setEnabled(true);
|
|
}
|
|
} else {
|
|
// Disabling
|
|
setEnabled(false);
|
|
}
|
|
};
|
|
|
|
if (permission === 'unsupported') {
|
|
return null;
|
|
}
|
|
|
|
const getIcon = () => {
|
|
if (!isEnabled) return <BellOff className="w-4 h-4" />;
|
|
if (permission === 'denied') return <BellOff className="w-4 h-4 text-red-400" />;
|
|
return <BellRing className="w-4 h-4" />;
|
|
};
|
|
|
|
const getStatus = () => {
|
|
if (permission === 'denied') return 'Blocked';
|
|
if (!isEnabled) return 'Off';
|
|
return 'On';
|
|
};
|
|
|
|
if (compact) {
|
|
return (
|
|
<button
|
|
onClick={handleToggle}
|
|
className={`flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors ${
|
|
isEnabled && permission === 'granted'
|
|
? 'bg-primary-500/20 text-primary-400 hover:bg-primary-500/30'
|
|
: 'bg-dark-700 text-dark-400 hover:bg-dark-600'
|
|
}`}
|
|
title={`Desktop notifications: ${getStatus()}`}
|
|
>
|
|
{getIcon()}
|
|
<span className="hidden sm:inline">{getStatus()}</span>
|
|
</button>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center justify-between p-3 bg-dark-750 rounded-lg">
|
|
<div className="flex items-center gap-3">
|
|
<div className={`p-2 rounded-lg ${
|
|
isEnabled && permission === 'granted'
|
|
? 'bg-primary-500/20 text-primary-400'
|
|
: 'bg-dark-700 text-dark-400'
|
|
}`}>
|
|
<Bell className="w-5 h-5" />
|
|
</div>
|
|
<div>
|
|
<div className="text-sm font-medium text-white">Desktop Notifications</div>
|
|
<div className="text-xs text-dark-400">
|
|
{permission === 'denied'
|
|
? 'Blocked by browser - enable in browser settings'
|
|
: isEnabled
|
|
? 'Get notified when new best solutions are found'
|
|
: 'Enable to receive optimization updates'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleToggle}
|
|
disabled={permission === 'denied'}
|
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
|
isEnabled && permission === 'granted'
|
|
? 'bg-primary-500'
|
|
: permission === 'denied'
|
|
? 'bg-dark-600 cursor-not-allowed'
|
|
: 'bg-dark-600'
|
|
}`}
|
|
>
|
|
<span
|
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
|
isEnabled && permission === 'granted' ? 'translate-x-6' : 'translate-x-1'
|
|
}`}
|
|
/>
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default NotificationSettings;
|