import { useState } from 'react' import { Brain, TrendingUp, TrendingDown, RefreshCw, Zap, AlertTriangle, BookOpen, Target, Eye, Clock, ChevronRight } from 'lucide-react' import clsx from 'clsx' import { usePortfolioReportData, useGeneratePortfolioReport, useAiReportsList, useAiReport } from '../hooks/useApi' import { useQueryClient } from '@tanstack/react-query' const SCENARIO_META: Record = { goldilocks: { label: 'Goldilocks', color: '#22c55e', emoji: '🌟' }, desinflation: { label: 'DĂ©sinflation', color: '#06b6d4', emoji: '❄' }, soft_landing: { label: 'Soft Landing', color: '#3b82f6', emoji: '🛬' }, reflation: { label: 'Reflation', color: '#f97316', emoji: 'đŸ”„' }, stagflation: { label: 'Stagflation', color: '#eab308', emoji: '⚡' }, inflation_shock: { label: 'Inflation Shock', color: '#ef4444', emoji: 'đŸ’„' }, recession: { label: 'RĂ©cession', color: '#8b5cf6', emoji: '📉' }, crise_liquidite: { label: 'Crise LiquiditĂ©', color: '#ec4899', emoji: '🚹' }, incertain: { label: 'Incertain', color: '#64748b', emoji: '❓' }, } function ScenarioBadge({ dominant }: { dominant: string }) { const m = SCENARIO_META[dominant] ?? SCENARIO_META.incertain return ( {m.emoji} {m.label} ) } function PnlBar({ pnl }: { pnl: number }) { const pos = pnl >= 0 const width = Math.min(Math.abs(pnl), 200) / 2 // cap at 100% width return (
{pos ? '+' : ''}{pnl.toFixed(1)}%
) } function ScoreTrend({ trend }: { trend: number[] }) { if (!trend || trend.length === 0) return — return (
{trend.map((s, i) => (
= 60 ? '#22c55e' : (s ?? 0) >= 40 ? '#eab308' : '#64748b', }} title={`${s}/100`} /> ))}
) } function MoverCard({ trade, rank, type }: { trade: any; rank: number; type: 'winner' | 'loser' }) { const [expanded, setExpanded] = useState(false) const pnl = trade.pnl_pct ?? 0 const sc = trade.scoring_context ?? {} const scOut = sc.output ?? {} const sg = trade.suggestion_context ?? {} const sgOut = sg.output ?? {} const buckets: any[] = scOut.buckets ?? [] const isWinner = type === 'winner' return (
{/* Rank */}
{rank}
{trade.pattern_name || trade.pattern_id} {trade.direction === 'bearish' ? 'đŸ»' : '🐂'} {trade.strategy} {trade.underlying}
Score entrĂ©e : {trade.score_at_entry ?? '?'}/100 EV : 0 ? 'text-emerald-400' : 'text-red-400')}> {trade.ev_net != null ? `${(trade.ev_net * 100).toFixed(0)}%` : '—'} {sc.macro_dominant && } {sc.geo_score != null && GĂ©o {sc.geo_score}/100} {trade.entry_date}
{scOut.key_catalyst && (

Catalyseur : {scOut.key_catalyst}

)} {trade.score_trend?.length > 0 && (
Score trend :
)} {expanded && (
{/* ThĂšse d'origine */} {(sgOut.macro_fit || sgOut.description) && (
ThĂšse initiale : {sgOut.macro_fit || sgOut.description}
)} {/* Piliers */} {buckets.length > 0 && (
{buckets.map((b: any, i: number) => { const pct = b.max ? Math.round((b.score / b.max) * 100) : 0 const color = pct >= 66 ? '#22c55e' : pct >= 33 ? '#eab308' : '#ef4444' return (
{b.label ?? b.id} {b.score}/{b.max}
) })}
)} {scOut.summary && (

{scOut.summary}

)}
)}
) } function ReportSection({ icon: Icon, title, content, color = 'text-slate-300' }: { icon: any; title: string; content: string | string[]; color?: string }) { return (
{title}
{Array.isArray(content) ? (
    {content.map((item, i) => (
  • ‱ {item}
  • ))}
) : (

{content}

)}
) } export default function RapportIA() { const [days, setDays] = useState(30) const [selectedHistoryId, setSelectedHistoryId] = useState(null) const [showHistory, setShowHistory] = useState(false) const queryClient = useQueryClient() const { data: rawData, isLoading: loadingRaw, refetch } = usePortfolioReportData(days) const { mutate: generate, isPending: generating, data: freshReportData, reset: resetFresh } = useGeneratePortfolioReport() const { data: historyList } = useAiReportsList() const { data: historicReport } = useAiReport(selectedHistoryId) const history: any[] = (historyList as any)?.reports ?? [] // Active report: either a loaded historic one, or the freshly generated one const activeHistoric = historicReport as any const fresh = freshReportData as any const raw = rawData as any const rep = selectedHistoryId && activeHistoric ? activeHistoric : fresh const report = rep?.report ?? null const stats = rep?.stats ?? raw const winners = rep?.winners ?? raw?.winners ?? [] const losers = rep?.losers ?? raw?.losers ?? [] const avgPnl = stats?.avg_pnl_pct ?? null function handleGenerate() { setSelectedHistoryId(null) resetFresh() generate(days, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ai-reports-list'] }) }, }) } return (
{/* History sidebar */} {showHistory && ( )}
{/* Header */}
{!showHistory && ( )}

Rapport IA — Performance & Analyse {selectedHistoryId && activeHistoric && ( archivĂ© · {activeHistoric.created_at?.slice(0, 10)} )}

SynthÚse des top mouvements + explication GPT-4o basée sur les traces de raisonnement

{selectedHistoryId && ( )} {/* PĂ©riode — only relevant for new generation */} {!selectedHistoryId && (
Période : {[7, 14, 30, 90].map(d => ( ))}
)} {!selectedHistoryId && ( )}
{/* Stats bar */} {(loadingRaw || stats) && (
{[ { label: 'Trades total', value: stats?.total_trades ?? '
' }, { label: 'Trades pricĂ©s', value: stats?.priced_count ?? '
' }, { label: 'P&L moyen', value: avgPnl != null ? = 0 ? 'text-emerald-400' : 'text-red-400')}> {avgPnl >= 0 ? '+' : ''}{avgPnl.toFixed(1)}% : '—' }, ].map(({ label, value }) => (
{value}
{label}
))}
)} {/* GPT-4o report */} {report && (
Analyse GPT-4o
{report.headline && (

{report.headline}

)}
{report.winners_analysis && ( )} {report.losers_analysis && ( )}
{report.regime_assessment && ( )} {report.key_lessons?.length > 0 && ( )} {report.blind_spots && ( )} {report.next_cycle_priorities && ( )} {report.risk_watch && ( )}
)} {/* Top movers */} {(winners.length > 0 || losers.length > 0) && (
{/* Winners */}

Top Gains

{winners.length === 0 ? (
Aucun trade pricé
) : ( winners.map((t: any, i: number) => ( )) )}
{/* Losers */}

Top Pertes

{losers.length === 0 ? (
Aucun trade pricé
) : ( losers.map((t: any, i: number) => ( )) )}
)} {!loadingRaw && !raw && !report && (

Cliquer "Générer rapport IA" pour lancer l'analyse GPT-4o

Le rapport compare les trades gagnants et perdants, identifie les patterns récurrents et génÚre des recommandations pour le prochain cycle.

)}
) }