feat: Add comprehensive optimization analytics dashboard

- Created /api/optimization/analyze endpoint with 7 SQL analyses
- Replaced old TP/SL page with comprehensive dashboard
- Analyses: Quality Score Distribution, Direction Performance, Blocked Signals, Runner Performance, ATR vs MFE, Indicator Versions, Data Collection Status
- Real-time refresh capability
- Actionable recommendations based on data thresholds
- Roadmap links at bottom
- Addresses user request for automated SQL analysis dashboard
This commit is contained in:
mindesbunister
2025-11-23 20:07:24 +01:00
parent 4dc42075cb
commit 11ae0938ba
2 changed files with 591 additions and 526 deletions

View File

@@ -1,572 +1,218 @@
'use client'
import { useEffect, useState } from 'react'
import Link from 'next/link'
interface TPSLAnalysis {
success: boolean
analysis?: {
totalTrades: number
winningTrades: number
losingTrades: number
winRate: number
avgWin: number
avgLoss: number
profitFactor: number
maeAnalysis: {
avgMAE: number
medianMAE: number
percentile25MAE: number
percentile75MAE: number
worstMAE: number
}
mfeAnalysis: {
avgMFE: number
medianMFE: number
percentile25MFE: number
percentile75MFE: number
bestMFE: number
}
currentLevels: {
tp1Percent: number
tp2Percent: number
slPercent: number
tp1HitRate: number
tp2HitRate: number
slHitRate: number
moneyLeftOnTable: number
}
recommendations: {
optimalTP1: number
optimalTP2: number
optimalSL: number
reasoning: {
tp1: string
tp2: string
sl: string
}
projectedImpact: {
expectedWinRateChange: number
expectedProfitFactorChange: number
estimatedProfitImprovement: number
}
}
tradesByOutcome: {
tp1Exits: number
tp2Exits: number
slExits: number
manualExits: number
}
dynamicATRAnalysis?: {
available: boolean
sampleSize: number
minSampleSize: number
sufficientData: boolean
avgATRPercent: number
dynamicTP2Percent: number
dynamicSLPercent: number
actualPnL: number
fixedSimulatedPnL: number
dynamicSimulatedPnL: number
dynamicAdvantage: number
dynamicAdvantagePercent: number
dynamicTP2HitRate: number
dynamicSLHitRate: number
recommendation: string
reasoning: string
}
}
error?: string
interface AnalysisResult {
name: string
description: string
status: 'loading' | 'success' | 'error'
data: any
recommendation?: string
action?: string
}
export default function OptimizationPage() {
const [analysis, setAnalysis] = useState<TPSLAnalysis | null>(null)
const [analyses, setAnalyses] = useState<AnalysisResult[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [lastRefresh, setLastRefresh] = useState<Date | null>(null)
useEffect(() => {
fetchAnalysis()
}, [])
const fetchAnalysis = async () => {
const loadAnalyses = async () => {
setLoading(true)
try {
setLoading(true)
setError(null)
const response = await fetch('/api/analytics/tp-sl-optimization')
const response = await fetch('/api/optimization/analyze')
const data = await response.json()
setAnalysis(data)
if (!data.success) {
setError(data.error || 'Failed to load analysis')
}
} catch (err) {
setError('Failed to fetch analytics: ' + (err as Error).message)
setAnalyses(data.analyses)
setLastRefresh(new Date())
} catch (error) {
console.error('Failed to load analyses:', error)
} finally {
setLoading(false)
}
}
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex items-center justify-center h-64">
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div>
<p className="text-gray-400">Loading optimization analysis...</p>
</div>
</div>
</div>
</div>
)
}
if (error || !analysis?.success) {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-yellow-900/20 border border-yellow-600 rounded-lg p-6">
<h2 className="text-xl font-semibold text-yellow-400 mb-2"> Insufficient Data</h2>
<p className="text-gray-300 mb-4">{error || analysis?.error}</p>
<p className="text-sm text-gray-400 mb-4">
Need at least 10 closed trades with MAE/MFE tracking data.
The next trades you take will automatically track this data.
</p>
<button
onClick={fetchAnalysis}
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-semibold transition-colors"
>
🔄 Refresh Analytics
</button>
</div>
</div>
</div>
)
}
const data = analysis.analysis!
useEffect(() => {
loadAnalyses()
}, [])
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header with Refresh */}
<div className="flex items-center justify-between mb-8">
<div>
<h2 className="text-2xl font-bold text-white">💡 TP/SL Optimization</h2>
<p className="text-sm text-gray-400 mt-1">Based on {data.totalTrades} trades with MAE/MFE data</p>
</div>
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-8">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="mb-8">
<h1 className="text-4xl font-bold mb-2">🔬 Optimization Analytics Dashboard</h1>
<p className="text-slate-400">
Data-driven analysis for trading system improvements based on roadmaps and SQL queries
</p>
{lastRefresh && (
<p className="text-sm text-slate-500 mt-2">
Last refresh: {lastRefresh.toLocaleTimeString()}
</p>
)}
</div>
{/* Refresh Button */}
<div className="mb-6">
<button
onClick={fetchAnalysis}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors text-white"
onClick={loadAnalyses}
disabled={loading}
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-slate-600 rounded-lg font-medium transition-colors"
>
🔄 Refresh
{loading ? '🔄 Analyzing...' : '🔄 Refresh All Analyses'}
</button>
</div>
{/* Overview Stats */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<StatCard title="Total Trades" value={data.totalTrades.toString()} />
<StatCard
title="Win Rate"
value={data.winRate.toFixed(1) + '%'}
valueColor="text-green-400"
/>
<StatCard
title="Profit Factor"
value={data.profitFactor.toFixed(2)}
valueColor="text-blue-400"
/>
<StatCard
title="Money Left on Table"
value={'$' + data.currentLevels.moneyLeftOnTable.toFixed(2)}
valueColor="text-yellow-400"
/>
</div>
{/* MAE/MFE Analysis */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<h3 className="text-xl font-semibold mb-4 text-green-400">
📈 Maximum Favorable Excursion (MFE)
</h3>
<div className="space-y-3">
<MetricRow label="Average" value={data.mfeAnalysis.avgMFE.toFixed(2) + '%'} />
<MetricRow label="Median" value={data.mfeAnalysis.medianMFE.toFixed(2) + '%'} />
<MetricRow label="25th Percentile" value={data.mfeAnalysis.percentile25MFE.toFixed(2) + '%'} />
<MetricRow label="75th Percentile" value={data.mfeAnalysis.percentile75MFE.toFixed(2) + '%'} />
<div className="border-t border-gray-700 pt-3">
<MetricRow
label="Best"
value={data.mfeAnalysis.bestMFE.toFixed(2) + '%'}
valueColor="text-green-400 font-bold"
/>
</div>
</div>
</div>
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<h3 className="text-xl font-semibold mb-4 text-red-400">
📉 Maximum Adverse Excursion (MAE)
</h3>
<div className="space-y-3">
<MetricRow label="Average" value={data.maeAnalysis.avgMAE.toFixed(2) + '%'} />
<MetricRow label="Median" value={data.maeAnalysis.medianMAE.toFixed(2) + '%'} />
<MetricRow label="25th Percentile" value={data.maeAnalysis.percentile25MAE.toFixed(2) + '%'} />
<MetricRow label="75th Percentile" value={data.maeAnalysis.percentile75MAE.toFixed(2) + '%'} />
<div className="border-t border-gray-700 pt-3">
<MetricRow
label="Worst"
value={data.maeAnalysis.worstMAE.toFixed(2) + '%'}
valueColor="text-red-400 font-bold"
/>
</div>
</div>
</div>
</div>
{/* Current Configuration Performance */}
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700 mb-8">
<h3 className="text-xl font-semibold mb-6 text-white">🎯 Current Configuration Performance</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<HitRateBar
label={'TP1: ' + data.currentLevels.tp1Percent + '%'}
hitRate={data.currentLevels.tp1HitRate}
exits={data.tradesByOutcome.tp1Exits}
color="bg-green-500"
/>
<HitRateBar
label={'TP2: ' + data.currentLevels.tp2Percent + '%'}
hitRate={data.currentLevels.tp2HitRate}
exits={data.tradesByOutcome.tp2Exits}
color="bg-blue-500"
/>
<HitRateBar
label={'SL: ' + data.currentLevels.slPercent + '%'}
hitRate={data.currentLevels.slHitRate}
exits={data.tradesByOutcome.slExits}
color="bg-red-500"
/>
</div>
</div>
{/* Dynamic ATR Analysis */}
{data.dynamicATRAnalysis?.available && (
<div className={`rounded-xl p-6 border mb-8 ${
data.dynamicATRAnalysis.sufficientData
? 'bg-gradient-to-r from-purple-900/30 to-blue-900/30 border-purple-700'
: 'bg-yellow-900/20 border-yellow-600'
}`}>
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-semibold text-white">
🎯 Dynamic ATR-Based TP/SL Analysis
</h3>
<div className="text-sm text-gray-400">
{data.dynamicATRAnalysis.sampleSize}/{data.dynamicATRAnalysis.minSampleSize} trades collected
</div>
</div>
{/* Progress Bar */}
<div className="mb-6">
<div className="flex justify-between mb-2">
<span className="text-sm text-gray-400">Data Collection Progress</span>
<span className="text-sm font-semibold text-white">
{((data.dynamicATRAnalysis.sampleSize / data.dynamicATRAnalysis.minSampleSize) * 100).toFixed(1)}%
</span>
</div>
<div className="bg-gray-700 rounded-full h-3 overflow-hidden">
<div
className={`h-full transition-all duration-500 ${
data.dynamicATRAnalysis.sufficientData ? 'bg-green-500' : 'bg-yellow-500'
}`}
style={{
width: Math.min((data.dynamicATRAnalysis.sampleSize / data.dynamicATRAnalysis.minSampleSize) * 100, 100) + '%'
}}
/>
</div>
</div>
{/* Comparison Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
{/* Left: Current Fixed Targets */}
<div className="bg-gray-800/50 rounded-lg p-4 border border-gray-700">
<h4 className="text-sm font-semibold text-gray-400 mb-3">📌 Fixed Targets (Current)</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">TP2:</span>
<span className="text-blue-400 font-semibold">{data.currentLevels.tp2Percent}%</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-400">SL:</span>
<span className="text-red-400 font-semibold">{data.currentLevels.slPercent}%</span>
</div>
<div className="border-t border-gray-700 pt-2 mt-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">Simulated P&L:</span>
<span className="text-white font-bold">
${data.dynamicATRAnalysis.fixedSimulatedPnL.toFixed(2)}
</span>
</div>
</div>
</div>
</div>
{/* Right: Dynamic ATR Targets */}
<div className="bg-purple-900/30 rounded-lg p-4 border border-purple-600">
<h4 className="text-sm font-semibold text-purple-300 mb-3"> Dynamic ATR-Based</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">TP2 (2x ATR):</span>
<span className="text-blue-400 font-semibold">
{data.dynamicATRAnalysis.dynamicTP2Percent.toFixed(2)}%
</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-400">SL (1.5x ATR):</span>
<span className="text-red-400 font-semibold">
{data.dynamicATRAnalysis.dynamicSLPercent.toFixed(2)}%
</span>
</div>
<div className="border-t border-purple-600 pt-2 mt-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">Simulated P&L:</span>
<span className="text-green-400 font-bold">
${data.dynamicATRAnalysis.dynamicSimulatedPnL.toFixed(2)}
</span>
</div>
</div>
</div>
</div>
</div>
{/* Advantage Highlight */}
<div className={`rounded-lg p-4 mb-4 ${
data.dynamicATRAnalysis.dynamicAdvantage >= 0
? 'bg-green-900/30 border border-green-600'
: 'bg-red-900/30 border border-red-600'
}`}>
<div className="flex items-center justify-between">
<div>
<div className="text-sm text-gray-400 mb-1">Dynamic ATR Advantage</div>
<div className="text-3xl font-bold text-white">
{data.dynamicATRAnalysis.dynamicAdvantage >= 0 ? '+' : ''}
${data.dynamicATRAnalysis.dynamicAdvantage.toFixed(2)}
<span className="text-xl ml-2 text-gray-400">
({data.dynamicATRAnalysis.dynamicAdvantagePercent >= 0 ? '+' : ''}
{data.dynamicATRAnalysis.dynamicAdvantagePercent.toFixed(1)}%)
</span>
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-400 mb-1">Avg ATR</div>
<div className="text-lg font-semibold text-white">
{data.dynamicATRAnalysis.avgATRPercent.toFixed(2)}%
</div>
</div>
</div>
</div>
{/* Recommendation */}
<div className={`rounded-lg p-4 border ${
data.dynamicATRAnalysis.sufficientData
? data.dynamicATRAnalysis.recommendation.includes('IMPLEMENT')
? 'bg-green-900/30 border-green-600'
: data.dynamicATRAnalysis.recommendation.includes('CONSIDER')
? 'bg-blue-900/30 border-blue-600'
: 'bg-gray-800/50 border-gray-600'
: 'bg-yellow-900/30 border-yellow-600'
}`}>
<div className="font-semibold text-white mb-2">
{data.dynamicATRAnalysis.recommendation}
</div>
<div className="text-sm text-gray-300">
{data.dynamicATRAnalysis.reasoning}
</div>
</div>
{/* Loading State */}
{loading && analyses.length === 0 && (
<div className="text-center py-20">
<div className="text-6xl mb-4"></div>
<p className="text-xl text-slate-400">Running SQL analyses...</p>
</div>
)}
{/* Recommendations */}
<div className="bg-gradient-to-r from-blue-900/30 to-purple-900/30 border border-blue-700 rounded-xl p-6 mb-8">
<h2 className="text-2xl font-bold mb-6 text-white">💡 Optimization Recommendations</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<RecommendationCard
label="Optimal TP1"
value={data.recommendations.optimalTP1.toFixed(2) + '%'}
current={data.currentLevels.tp1Percent + '%'}
color="text-green-400"
{/* Analysis Results */}
<div className="space-y-6">
{analyses.map((analysis, index) => (
<AnalysisCard key={index} analysis={analysis} />
))}
</div>
{/* Roadmap Links */}
<div className="mt-12 p-6 bg-slate-800/50 rounded-lg border border-slate-700">
<h2 className="text-2xl font-bold mb-4">📚 Optimization Roadmaps</h2>
<div className="grid md:grid-cols-3 gap-4">
<RoadmapLink
title="Signal Quality"
file="SIGNAL_QUALITY_OPTIMIZATION_ROADMAP.md"
status="Phase 1 - Collecting Data"
progress="8/20 blocked signals"
/>
<RecommendationCard
label="Optimal TP2"
value={data.recommendations.optimalTP2.toFixed(2) + '%'}
current={data.currentLevels.tp2Percent + '%'}
color="text-blue-400"
<RoadmapLink
title="Position Scaling"
file="POSITION_SCALING_ROADMAP.md"
status="Phase 1 - Data Collection"
progress="Collecting MAE/MFE data"
/>
<RecommendationCard
label="Optimal SL"
value={data.recommendations.optimalSL.toFixed(2) + '%'}
current={data.currentLevels.slPercent + '%'}
color="text-red-400"
<RoadmapLink
title="ATR-Based Targets"
file="ATR_BASED_TP_ROADMAP.md"
status="Phase 1 - Collecting ATR"
progress="Need 50+ trades"
/>
</div>
</div>
</div>
</div>
)
}
{/* Reasoning */}
<div className="space-y-3 mb-6">
<ReasoningCard
label="TP1 Reasoning"
text={data.recommendations.reasoning.tp1}
color="border-green-700 bg-green-900/20"
/>
<ReasoningCard
label="TP2 Reasoning"
text={data.recommendations.reasoning.tp2}
color="border-blue-700 bg-blue-900/20"
/>
<ReasoningCard
label="SL Reasoning"
text={data.recommendations.reasoning.sl}
color="border-red-700 bg-red-900/20"
/>
</div>
function AnalysisCard({ analysis }: { analysis: AnalysisResult }) {
const statusColors = {
loading: 'border-yellow-500 bg-yellow-500/10',
success: 'border-green-500 bg-green-500/10',
error: 'border-red-500 bg-red-500/10'
}
{/* Projected Impact */}
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<h3 className="text-lg font-semibold mb-4 text-white">📊 Projected Impact</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<ImpactMetric
label="Win Rate Change"
value={data.recommendations.projectedImpact.expectedWinRateChange.toFixed(1) + '%'}
positive={data.recommendations.projectedImpact.expectedWinRateChange >= 0}
/>
<ImpactMetric
label="Profit Factor Change"
value={data.recommendations.projectedImpact.expectedProfitFactorChange.toFixed(2)}
positive={data.recommendations.projectedImpact.expectedProfitFactorChange >= 0}
/>
<ImpactMetric
label="Profit Improvement"
value={data.recommendations.projectedImpact.estimatedProfitImprovement.toFixed(1) + '%'}
positive={data.recommendations.projectedImpact.estimatedProfitImprovement >= 0}
/>
const statusIcons = {
loading: '⏳',
success: '✅',
error: '❌'
}
return (
<div className={`p-6 rounded-lg border-2 ${statusColors[analysis.status]}`}>
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<h3 className="text-xl font-bold mb-1 flex items-center gap-2">
{statusIcons[analysis.status]} {analysis.name}
</h3>
<p className="text-sm text-slate-400">{analysis.description}</p>
</div>
</div>
{/* Data Display */}
{analysis.status === 'success' && analysis.data && (
<div className="mt-4">
{/* Table */}
{Array.isArray(analysis.data) && analysis.data.length > 0 && (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-slate-700">
{Object.keys(analysis.data[0]).map(key => (
<th key={key} className="text-left py-2 px-3 font-medium text-slate-300">
{key}
</th>
))}
</tr>
</thead>
<tbody>
{analysis.data.map((row: any, i: number) => (
<tr key={i} className="border-b border-slate-800">
{Object.values(row).map((value: any, j: number) => (
<td key={j} className="py-2 px-3">
{typeof value === 'number' ? value.toFixed(2) : String(value)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{/* Summary Stats */}
{typeof analysis.data === 'object' && !Array.isArray(analysis.data) && (
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{Object.entries(analysis.data).map(([key, value]) => (
<div key={key} className="bg-slate-800/50 p-4 rounded">
<div className="text-xs text-slate-400 mb-1">{key}</div>
<div className="text-2xl font-bold">
{typeof value === 'number' ? value.toFixed(2) : String(value)}
</div>
</div>
))}
</div>
)}
</div>
)}
{/* Action Button */}
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700 text-center">
<p className="text-gray-400 mb-4">
Ready to apply these optimized levels? Update your configuration in Settings.
</p>
<Link
href="/settings"
className="inline-block px-8 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-semibold transition-colors text-white"
>
Go to Settings
</Link>
{/* Recommendation */}
{analysis.recommendation && (
<div className="mt-4 p-4 bg-blue-900/30 border border-blue-500/30 rounded-lg">
<div className="text-sm font-medium text-blue-300 mb-1">💡 Recommendation</div>
<p className="text-sm">{analysis.recommendation}</p>
</div>
</div>
</div>
)
}
)}
// Component helpers
function Header() {
return (
<div className="bg-gray-800/50 backdrop-blur-sm border-b border-gray-700">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div className="flex items-center space-x-4">
<Link href="/analytics" className="text-gray-400 hover:text-white transition">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</Link>
<div>
<h1 className="text-2xl font-bold text-white">🎯 TP/SL Optimization</h1>
<p className="text-sm text-gray-400">Data-driven recommendations for optimal exit levels</p>
</div>
{/* Action */}
{analysis.action && (
<div className="mt-4 p-4 bg-purple-900/30 border border-purple-500/30 rounded-lg">
<div className="text-sm font-medium text-purple-300 mb-1">🎯 Action Required</div>
<p className="text-sm">{analysis.action}</p>
</div>
</div>
)}
{/* Error */}
{analysis.status === 'error' && (
<div className="mt-4 p-4 bg-red-900/30 border border-red-500/30 rounded-lg">
<p className="text-sm text-red-300">{analysis.data?.error || 'Analysis failed'}</p>
</div>
)}
</div>
)
}
function StatCard({ title, value, valueColor = 'text-white' }: { title: string, value: string, valueColor?: string }) {
function RoadmapLink({ title, file, status, progress }: {
title: string
file: string
status: string
progress: string
}) {
return (
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<div className="text-sm text-gray-400 mb-1">{title}</div>
<div className={'text-2xl font-bold ' + valueColor}>{value}</div>
</div>
)
}
function MetricRow({ label, value, valueColor = 'text-white' }: { label: string, value: string, valueColor?: string }) {
return (
<div className="flex justify-between">
<span className="text-gray-400">{label}:</span>
<span className={'font-semibold ' + valueColor}>{value}</span>
</div>
)
}
function HitRateBar({ label, hitRate, exits, color }: { label: string, hitRate: number, exits: number, color: string }) {
return (
<div>
<div className="text-sm text-gray-400 mb-2">{label}</div>
<div className="bg-gray-700 rounded-full h-4 overflow-hidden">
<div
className={color + ' h-full transition-all duration-500'}
style={{ width: hitRate + '%' }}
/>
</div>
<div className="flex justify-between mt-1">
<div className="text-xs text-gray-400">Hit Rate: {hitRate.toFixed(1)}%</div>
<div className="text-xs text-gray-500">{exits} exits</div>
</div>
</div>
)
}
function RecommendationCard({ label, value, current, color }: { label: string, value: string, current: string, color: string }) {
return (
<div className="bg-gray-800/50 rounded-lg p-4 border border-gray-700">
<div className="text-sm text-gray-400 mb-1">{label}</div>
<div className={'text-3xl font-bold ' + color}>{value}</div>
<div className="text-xs text-gray-400 mt-2">Current: {current}</div>
</div>
)
}
function ReasoningCard({ label, text, color }: { label: string, text: string, color: string }) {
return (
<div className={'rounded-lg p-4 border ' + color}>
<div className="font-semibold text-white mb-1">{label}</div>
<div className="text-sm text-gray-300">{text}</div>
</div>
)
}
function ImpactMetric({ label, value, positive }: { label: string, value: string, positive: boolean }) {
return (
<div>
<div className="text-sm text-gray-400 mb-1">{label}</div>
<div className={'text-2xl font-bold ' + (positive ? 'text-green-400' : 'text-red-400')}>
{positive ? '+' : ''}{value}
</div>
<div className="p-4 bg-slate-700/30 rounded-lg border border-slate-600 hover:border-slate-500 transition-colors">
<h3 className="font-bold mb-2">{title}</h3>
<p className="text-xs text-slate-400 mb-1">{status}</p>
<p className="text-xs text-green-400">{progress}</p>
</div>
)
}