583 lines
27 KiB
JavaScript
583 lines
27 KiB
JavaScript
import { useState } from 'react';
|
||
import { Badge } from '@/components/ui/Badge';
|
||
import { Button } from '@/components/ui/button';
|
||
import { cn } from '@/utils/cn';
|
||
import { useAIAnalysis } from '@/hooks/useAIAnalysis';
|
||
import { X } from 'lucide-react';
|
||
|
||
/**
|
||
* Trade Analysis Modal
|
||
* Full-screen detailed analysis for evaluating individual trades
|
||
*/
|
||
export function TradeAnalysisModal({ row, isOpen, onClose }) {
|
||
const [showAIAnalysis, setShowAIAnalysis] = useState(false);
|
||
const { loading: aiLoading, error: aiError, analysis: aiAnalysis, fetchAnalysis } = useAIAnalysis();
|
||
|
||
if (!isOpen || !row) return null;
|
||
|
||
const signal = row.tradeSignal;
|
||
const symbol = row.Symbol || row.symbol_norm;
|
||
const score = row.rocketScore || row.rocket_score || 0;
|
||
const momentum = row.momentumScore || 0;
|
||
const netPremium = (row.bull_total || 0) - (row.bear_total || 0);
|
||
const bullTotal = row.bull_total || 0;
|
||
const bearTotal = row.bear_total || 0;
|
||
const premium = row.premium_num || 0;
|
||
const volume = row.Volume || row.vol_num || 0;
|
||
const oi = row.OI || row.oi_num || 0;
|
||
const currentPrice = parseFloat(row.u_close || row.spot_num || row.Price || 0) || 0;
|
||
// Ensure strike is always a number - handle string values like "150.00" or "$150"
|
||
const strikeValue = row.Strike || 0;
|
||
const strike = typeof strikeValue === 'string'
|
||
? parseFloat(strikeValue.replace(/[^0-9.-]/g, '')) || 0
|
||
: parseFloat(strikeValue) || 0;
|
||
const callPut = row.CallPut || row.cp_norm || '';
|
||
const expiration = row.ExpirationDate;
|
||
const moneyness = row.moneynessContext || {};
|
||
const isGamma = row.isGammaSqueezeSetup;
|
||
const tapeAlign = row.TapeAlign || row.tapeArrow || '—';
|
||
const trend = row.flowTrendRaw || row.flowTrend?.full;
|
||
const decay = row.flowDecayRaw || row.flowDecay?.full;
|
||
const catalyst = row.near_alert_type || 'None';
|
||
const badges = row.badges || row.badgesRaw || {};
|
||
|
||
// Extract prices
|
||
const extractPrice = (value) => {
|
||
if (!value) return null;
|
||
if (typeof value === 'string') {
|
||
const match = value.match(/\$?([\d.]+)/);
|
||
return match ? parseFloat(match[1]) : null;
|
||
}
|
||
if (typeof value === 'object') {
|
||
const primary = value.primary || value.aggressive || value.tight || value.wide || value.t1;
|
||
if (typeof primary === 'string') {
|
||
const match = primary.match(/\$?([\d.]+)/);
|
||
return match ? parseFloat(match[1]) : null;
|
||
}
|
||
return primary;
|
||
}
|
||
return null;
|
||
};
|
||
|
||
const entryPrice = signal?.entry ? extractPrice(signal.entry) : currentPrice;
|
||
const stopPrice = signal?.stop ? extractPrice(signal.stop) : null;
|
||
const target1Price = signal?.target?.t1 ? extractPrice(signal.target.t1) : null;
|
||
const target2Price = signal?.target?.t2 ? extractPrice(signal.target.t2) : null;
|
||
const target3Price = signal?.target?.t3 ? extractPrice(signal.target.t3) : null;
|
||
|
||
// Calculate R:R
|
||
const risk = entryPrice && stopPrice ? Math.abs(entryPrice - stopPrice) : null;
|
||
const reward1 = entryPrice && target1Price ? Math.abs(target1Price - entryPrice) : null;
|
||
const riskReward1 = risk && reward1 ? (reward1 / risk).toFixed(2) : null;
|
||
|
||
// Calculate win probability estimate
|
||
const winProb = signal?.confidence || 50;
|
||
|
||
// Get flow trend status
|
||
const getFlowTrend = (lastFlowMinutes) => {
|
||
if (lastFlowMinutes === null || lastFlowMinutes === undefined) {
|
||
return null;
|
||
}
|
||
if (lastFlowMinutes <= 5) {
|
||
return {
|
||
icon: '🔥',
|
||
label: 'SURGING',
|
||
color: 'red',
|
||
message: 'Institutions actively buying RIGHT NOW'
|
||
};
|
||
} else if (lastFlowMinutes <= 30) {
|
||
return {
|
||
icon: '🟡',
|
||
label: 'STABLE',
|
||
color: 'yellow',
|
||
message: 'Recent flow - institutions still positioned'
|
||
};
|
||
} else if (lastFlowMinutes <= 60) {
|
||
return {
|
||
icon: '⚠️',
|
||
label: 'FADING',
|
||
color: 'orange',
|
||
message: 'Flow slowing down - watch for reversal'
|
||
};
|
||
} else {
|
||
return {
|
||
icon: '💀',
|
||
label: 'DEAD',
|
||
color: 'red',
|
||
message: 'Flow too stale - institutions likely exited'
|
||
};
|
||
}
|
||
};
|
||
|
||
const lastFlowMinutesAgo = trend?.lastFlowMinutesAgo ?? decay?.lastFlowMinutesAgo;
|
||
const flowTrend = getFlowTrend(lastFlowMinutesAgo);
|
||
const hoursAgo = lastFlowMinutesAgo ? Math.floor(lastFlowMinutesAgo / 60) : 0;
|
||
|
||
return (
|
||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
||
<div className="w-full max-w-6xl max-h-[90vh] bg-slate-900 border border-slate-800 rounded-lg shadow-2xl overflow-hidden flex flex-col">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between p-6 border-b border-slate-800 bg-slate-950/50">
|
||
<div className="flex items-center gap-4">
|
||
<div>
|
||
<h2 className="text-2xl font-bold text-slate-100">{symbol}</h2>
|
||
<div className="flex items-center gap-2 mt-1">
|
||
<Badge
|
||
variant="default"
|
||
className={cn(
|
||
"text-xs",
|
||
callPut === 'CALL'
|
||
? "bg-blue-500/20 text-blue-400 border-blue-500/50"
|
||
: "bg-red-500/20 text-red-400 border-red-500/50"
|
||
)}
|
||
>
|
||
{callPut} ${typeof strike === 'number' ? strike.toFixed(2) : 'N/A'}
|
||
</Badge>
|
||
{isGamma && (
|
||
<Badge className="bg-purple-500/20 text-purple-400 border-purple-500/50 text-xs">
|
||
⚡ GAMMA SQUEEZE
|
||
</Badge>
|
||
)}
|
||
{signal?.type && (
|
||
<Badge variant="outline" className="text-xs">
|
||
{signal.type.replace(/_/g, ' ')}
|
||
</Badge>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={onClose}
|
||
className="text-slate-400 hover:text-slate-200"
|
||
>
|
||
<X className="w-5 h-5" />
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Content - Scrollable */}
|
||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
||
{/* Quick Stats Bar */}
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<div className="text-xs text-slate-500 mb-1">🚀 Score</div>
|
||
<div className="text-2xl font-bold text-slate-100">{score.toFixed(1)}</div>
|
||
</div>
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<div className="text-xs text-slate-500 mb-1">🔥 Momentum</div>
|
||
<div className="text-2xl font-bold text-slate-100">{momentum}</div>
|
||
</div>
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<div className="text-xs text-slate-500 mb-1">📊 Confidence</div>
|
||
<div className="text-2xl font-bold text-slate-100">{winProb}%</div>
|
||
</div>
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<div className="text-xs text-slate-500 mb-1">💰 Premium</div>
|
||
<div className="text-2xl font-bold text-slate-100">
|
||
${(premium / 1000000).toFixed(2)}M
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Trade Signal Card */}
|
||
{signal && signal.signal !== 'NEUTRAL' && signal.signal !== 'WAIT' && (
|
||
<div className="bg-gradient-to-r from-blue-500/10 to-purple-500/10 border border-blue-500/30 rounded-lg p-6">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="text-lg font-semibold text-slate-100">🎯 TRADE PLAN</h3>
|
||
<Badge
|
||
variant={signal.signal === 'BUY' ? 'success' : 'destructive'}
|
||
className="text-sm px-3 py-1"
|
||
>
|
||
{signal.signal === 'BUY' ? '🚀 BUY' : '🔴 SHORT'}
|
||
</Badge>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||
{entryPrice && typeof entryPrice === 'number' && (
|
||
<div className="bg-slate-800/70 rounded-lg p-4">
|
||
<div className="text-xs text-slate-400 mb-1">Entry</div>
|
||
<div className="text-xl font-bold text-green-400">${entryPrice.toFixed(2)}</div>
|
||
{signal.entry?.notes && (
|
||
<div className="text-xs text-slate-500 mt-1">{signal.entry.notes}</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
{stopPrice && typeof stopPrice === 'number' && (
|
||
<div className="bg-slate-800/70 rounded-lg p-4">
|
||
<div className="text-xs text-slate-400 mb-1">Stop Loss</div>
|
||
<div className="text-xl font-bold text-red-400">${stopPrice.toFixed(2)}</div>
|
||
{risk && typeof risk === 'number' && entryPrice && typeof entryPrice === 'number' && (
|
||
<div className="text-xs text-slate-500 mt-1">
|
||
Risk: ${risk.toFixed(2)} ({((risk / entryPrice) * 100).toFixed(1)}%)
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
{target1Price && typeof target1Price === 'number' && (
|
||
<div className="bg-slate-800/70 rounded-lg p-4">
|
||
<div className="text-xs text-slate-400 mb-1">Target 1</div>
|
||
<div className="text-xl font-bold text-blue-400">${target1Price.toFixed(2)}</div>
|
||
<div className="text-xs text-slate-500 mt-1">
|
||
{riskReward1 && `R:R ${riskReward1}:1`} • Take 50%
|
||
</div>
|
||
</div>
|
||
)}
|
||
{target2Price && typeof target2Price === 'number' && (
|
||
<div className="bg-slate-800/70 rounded-lg p-4">
|
||
<div className="text-xs text-slate-400 mb-1">Target 2</div>
|
||
<div className="text-xl font-bold text-blue-400">${target2Price.toFixed(2)}</div>
|
||
<div className="text-xs text-slate-500 mt-1">Take 30%</div>
|
||
</div>
|
||
)}
|
||
{target3Price && typeof target3Price === 'number' && (
|
||
<div className="bg-slate-800/70 rounded-lg p-4">
|
||
<div className="text-xs text-slate-400 mb-1">Target 3</div>
|
||
<div className="text-xl font-bold text-blue-400">${target3Price.toFixed(2)}</div>
|
||
<div className="text-xs text-slate-500 mt-1">Runner 20%</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{signal.reasoning && (
|
||
<div className="mt-4 p-4 bg-slate-800/50 rounded-lg">
|
||
<div className="text-xs text-slate-400 mb-2">Reasoning:</div>
|
||
<div className="text-sm text-slate-200">{signal.reasoning}</div>
|
||
</div>
|
||
)}
|
||
|
||
{signal.invalidation && (
|
||
<div className="mt-4 p-4 bg-red-500/10 border border-red-500/30 rounded-lg">
|
||
<div className="text-xs text-red-400 mb-2 font-semibold">⚠️ Invalidation Rules:</div>
|
||
<div className="text-sm text-slate-300">
|
||
{Array.isArray(signal.invalidation) ? (
|
||
<ul className="list-disc list-inside space-y-1">
|
||
{signal.invalidation.map((rule, idx) => (
|
||
<li key={idx}>{rule}</li>
|
||
))}
|
||
</ul>
|
||
) : (
|
||
<div>{signal.invalidation}</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* Flow Details */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<h4 className="text-sm font-semibold text-slate-300 mb-3">🔹 FLOW DETAILS</h4>
|
||
|
||
{/* Flow Trend Visual */}
|
||
{flowTrend && (
|
||
<div className={cn(
|
||
"mb-4 p-4 border-2 rounded-lg",
|
||
flowTrend.color === 'red' && flowTrend.label === 'DEAD'
|
||
? "bg-red-900/30 border-red-500"
|
||
: flowTrend.color === 'red' && flowTrend.label === 'SURGING'
|
||
? "bg-red-900/30 border-red-500"
|
||
: flowTrend.color === 'yellow'
|
||
? "bg-yellow-900/30 border-yellow-500"
|
||
: "bg-orange-900/30 border-orange-500"
|
||
)}>
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-5xl">{flowTrend.icon}</span>
|
||
<div>
|
||
<div className={cn(
|
||
"text-2xl font-bold",
|
||
flowTrend.color === 'red' ? "text-red-400" :
|
||
flowTrend.color === 'yellow' ? "text-yellow-400" :
|
||
"text-orange-400"
|
||
)}>
|
||
{flowTrend.label} FLOW
|
||
</div>
|
||
<div className="text-sm text-gray-300">
|
||
Last activity: <span className={cn(
|
||
"font-bold",
|
||
flowTrend.color === 'red' ? "text-red-300" :
|
||
flowTrend.color === 'yellow' ? "text-yellow-300" :
|
||
"text-orange-300"
|
||
)}>
|
||
{lastFlowMinutesAgo} minutes ago {hoursAgo > 0 && `(${hoursAgo} ${hoursAgo === 1 ? 'hour' : 'hours'})`}
|
||
</span>
|
||
</div>
|
||
<div className={cn(
|
||
"text-sm mt-1",
|
||
flowTrend.color === 'red' ? "text-yellow-300" :
|
||
flowTrend.color === 'yellow' ? "text-yellow-200" :
|
||
"text-orange-200"
|
||
)}>
|
||
⚠️ {flowTrend.message}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Net Premium:</span>
|
||
<span className={cn(
|
||
"font-semibold",
|
||
netPremium > 0 ? "text-green-400" : "text-red-400"
|
||
)}>
|
||
${(Math.abs(netPremium) / 1000000).toFixed(2)}M
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Bull Premium:</span>
|
||
<span className="text-green-400">${(bullTotal / 1000000).toFixed(2)}M</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Bear Premium:</span>
|
||
<span className="text-red-400">${(bearTotal / 1000000).toFixed(2)}M</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Volume:</span>
|
||
<span className="text-slate-300">{volume.toLocaleString()}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Open Interest:</span>
|
||
<span className="text-slate-300">{oi.toLocaleString()}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Moneyness:</span>
|
||
<span className="text-slate-300">
|
||
{row.moneynessLabel || `${moneyness.distancePct || 0}% ${moneyness.category || ''}`}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<h4 className="text-sm font-semibold text-slate-300 mb-3">📊 PRICE CONTEXT</h4>
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Current Price:</span>
|
||
<span className="text-slate-200 font-semibold">
|
||
${typeof currentPrice === 'number' ? currentPrice.toFixed(2) : 'N/A'}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Strike:</span>
|
||
<span className="text-slate-200">
|
||
${typeof strike === 'number' ? strike.toFixed(2) : 'N/A'}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Distance:</span>
|
||
<span className={cn(
|
||
"font-semibold",
|
||
moneyness.distancePct > 0 ? "text-red-400" : "text-green-400"
|
||
)}>
|
||
{moneyness.distancePct ? `${moneyness.distancePct > 0 ? '+' : ''}${moneyness.distancePct.toFixed(1)}%` : 'N/A'}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">vs RTH Open:</span>
|
||
<span className={cn(
|
||
"font-semibold",
|
||
(row.PctVsRthOpen || 0) > 0 ? "text-green-400" : "text-red-400"
|
||
)}>
|
||
{(row.PctVsRthOpen || 0) > 0 ? '+' : ''}{(row.PctVsRthOpen || 0).toFixed(2)}%
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-slate-500">Flow Trend:</span>
|
||
<span className={cn(
|
||
"font-semibold",
|
||
trend?.trend === 'SURGING' ? "text-green-400" :
|
||
trend?.trend === 'FADING' ? "text-red-400" :
|
||
"text-yellow-400"
|
||
)}>
|
||
{trend?.status || 'STABLE'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tape Alignment Visual */}
|
||
{tapeAlign !== '—' ? (
|
||
<div className="mt-4 p-4 bg-green-900/30 border-2 border-green-500 rounded-lg">
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-4xl">✅</span>
|
||
<div>
|
||
<div className="text-xl font-bold text-green-400">TAPE ALIGNED</div>
|
||
<div className="text-sm text-gray-300">Price confirming flow direction ↑</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div className="mt-4 p-4 bg-orange-900/30 border-2 border-orange-500 rounded-lg">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-4xl">⚠️</span>
|
||
<div>
|
||
<div className="text-xl font-bold text-orange-400">NO TAPE ALIGNMENT</div>
|
||
<div className="text-sm text-gray-300">Price not confirming flow direction</div>
|
||
</div>
|
||
</div>
|
||
<div className="text-right">
|
||
<div className="text-xs text-gray-400">Current Price</div>
|
||
<div className="text-2xl font-bold text-orange-300">${typeof currentPrice === 'number' ? currentPrice.toFixed(2) : 'N/A'}</div>
|
||
<div className="text-xs text-gray-400">Expected: Upward momentum</div>
|
||
<div className="text-xs text-red-300">Reality: Flat/down</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Badges & Catalyst */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<h4 className="text-sm font-semibold text-slate-300 mb-3">🏆 BADGES</h4>
|
||
<div className="flex flex-wrap gap-2">
|
||
{badges.round && (
|
||
<Badge variant="outline" className="text-lg">{badges.round}</Badge>
|
||
)}
|
||
{badges.more && badges.more.split(' ').map((badge, idx) => (
|
||
<Badge key={idx} variant="outline" className="text-lg">{badge}</Badge>
|
||
))}
|
||
{badges.flash && (
|
||
<Badge variant="outline" className="text-lg">{badges.flash}</Badge>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<h4 className="text-sm font-semibold text-slate-300 mb-3">⚡ CATALYST</h4>
|
||
{catalyst !== 'None' ? (
|
||
<div>
|
||
<Badge variant="warning" className="text-sm mb-2">{catalyst}</Badge>
|
||
{trend?.lastFlowMinutesAgo !== null && (
|
||
<div className="text-sm text-slate-400 mt-2">
|
||
Last flow: {trend.lastFlowMinutesAgo}min ago
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="text-slate-500 text-sm">No alerts detected</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* AI Analysis */}
|
||
<div className="bg-slate-800/50 rounded-lg p-4">
|
||
<div className="flex items-center justify-between mb-3">
|
||
<h4 className="text-sm font-semibold text-slate-300">🤖 AI ANALYSIS</h4>
|
||
{!showAIAnalysis && (
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => {
|
||
setShowAIAnalysis(true);
|
||
fetchAnalysis(row);
|
||
}}
|
||
disabled={aiLoading}
|
||
className="text-xs"
|
||
>
|
||
{aiLoading ? 'Analyzing...' : 'Get AI Analysis'}
|
||
</Button>
|
||
)}
|
||
</div>
|
||
|
||
{showAIAnalysis && (
|
||
<div className="space-y-3">
|
||
{aiLoading && (
|
||
<div className="text-sm text-slate-400">Analyzing with Claude AI...</div>
|
||
)}
|
||
|
||
{aiError && (
|
||
<div className="text-sm text-red-400">Error: {aiError}</div>
|
||
)}
|
||
|
||
{aiAnalysis && aiAnalysis.analysis && (
|
||
<div className="space-y-4">
|
||
<div className="text-sm text-slate-200 bg-slate-700/30 p-3 rounded border border-slate-600/50">
|
||
<strong>Summary:</strong> {aiAnalysis.analysis.summary}
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
{/* Long Term */}
|
||
{aiAnalysis.analysis.longTerm && (
|
||
<div className="bg-slate-900/50 p-3 rounded-lg border border-slate-700/50">
|
||
<div className="text-xs font-bold text-slate-400 mb-2 uppercase tracking-wider">Long-Term Investor</div>
|
||
<div className="flex gap-2 mb-2">
|
||
<Badge variant={aiAnalysis.analysis.longTerm.assessment === 'BULLISH' ? 'success' : aiAnalysis.analysis.longTerm.assessment === 'BEARISH' ? 'destructive' : 'secondary'} className="text-[10px]">
|
||
{aiAnalysis.analysis.longTerm.assessment}
|
||
</Badge>
|
||
<Badge variant={aiAnalysis.analysis.longTerm.recommendation === 'ENTER' ? 'success' : aiAnalysis.analysis.longTerm.recommendation === 'WAIT' ? 'warning' : 'destructive'} className="text-[10px]">
|
||
{aiAnalysis.analysis.longTerm.recommendation}
|
||
</Badge>
|
||
</div>
|
||
<p className="text-xs text-slate-300 leading-relaxed">{aiAnalysis.analysis.longTerm.moatAndFundamentals}</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* Swing Trade */}
|
||
{aiAnalysis.analysis.swing && (
|
||
<div className="bg-slate-900/50 p-3 rounded-lg border border-slate-700/50">
|
||
<div className="text-xs font-bold text-slate-400 mb-2 uppercase tracking-wider">Swing Trader</div>
|
||
<div className="flex gap-2 mb-2">
|
||
<Badge variant={aiAnalysis.analysis.swing.assessment === 'BULLISH' ? 'success' : aiAnalysis.analysis.swing.assessment === 'BEARISH' ? 'destructive' : 'secondary'} className="text-[10px]">
|
||
{aiAnalysis.analysis.swing.assessment}
|
||
</Badge>
|
||
<Badge variant={aiAnalysis.analysis.swing.recommendation === 'ENTER' ? 'success' : aiAnalysis.analysis.swing.recommendation === 'WAIT' ? 'warning' : 'destructive'} className="text-[10px]">
|
||
{aiAnalysis.analysis.swing.recommendation}
|
||
</Badge>
|
||
</div>
|
||
<p className="text-xs text-slate-300 leading-relaxed">{aiAnalysis.analysis.swing.technicalSetup}</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* Day Trade */}
|
||
{aiAnalysis.analysis.dayTrade && (
|
||
<div className="bg-slate-900/50 p-3 rounded-lg border border-slate-700/50">
|
||
<div className="text-xs font-bold text-slate-400 mb-2 uppercase tracking-wider">Day Trader</div>
|
||
<div className="flex gap-2 mb-2">
|
||
<Badge variant={aiAnalysis.analysis.dayTrade.assessment === 'BULLISH' ? 'success' : aiAnalysis.analysis.dayTrade.assessment === 'BEARISH' ? 'destructive' : 'secondary'} className="text-[10px]">
|
||
{aiAnalysis.analysis.dayTrade.assessment}
|
||
</Badge>
|
||
<Badge variant={aiAnalysis.analysis.dayTrade.recommendation === 'ENTER' ? 'success' : aiAnalysis.analysis.dayTrade.recommendation === 'WAIT' ? 'warning' : 'destructive'} className="text-[10px]">
|
||
{aiAnalysis.analysis.dayTrade.recommendation}
|
||
</Badge>
|
||
</div>
|
||
<p className="text-xs text-slate-300 leading-relaxed">{aiAnalysis.analysis.dayTrade.volatilityAndLiquidity}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Footer Actions */}
|
||
<div className="flex items-center justify-between p-4 border-t border-slate-800 bg-slate-950/50">
|
||
<div className="text-xs text-slate-500">
|
||
Time Horizon: {signal?.timeHorizon || 'INTRADAY'} •
|
||
Expiration: {expiration ? new Date(expiration).toLocaleDateString() : 'N/A'}
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<Button variant="outline" size="sm" onClick={onClose}>
|
||
Close
|
||
</Button>
|
||
<Button
|
||
variant="default"
|
||
size="sm"
|
||
className="bg-blue-600 hover:bg-blue-700"
|
||
>
|
||
Add to Watchlist
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|