import 'package:flutter/material.dart'; import '../../../core/theme/app_theme.dart'; import '../models/trade_model.dart'; import 'trade_calculation_card.dart'; /// Migrated onto `ActiveTradeDto`. The old "KI-Analysen, Bewertungen & /// Begründungen" expansion (reasoning/technicalRationale/ /// fundamentalRationale/riskWarning, plus the hourly AI-Guardian check-in /// timeline) has no backend equivalent anymore — none of those fields exist /// on `ActiveTradeDto`, so the section was removed rather than shown empty /// (Rules.md §4). This does leave the detail view noticeably thinner than /// before: today it can only show the mechanical trade state (prices, exit /// plan, fills), not any narrative "why" behind the trade. class TradeDetailContent extends StatelessWidget { final TradeModel trade; const TradeDetailContent({super.key, required this.trade}); String _fmt(double val) => val.toStringAsFixed(2); @override Widget build(BuildContext context) { final pnlAbs = trade.pnlEur; final pnlPct = trade.unrealizedPnlPercent; final isPnlPos = pnlAbs >= 0; final pnlColor = isPnlPos ? AppTheme.primaryEmerald : AppTheme.accentRed; final currPrice = trade.currentPrice; final tpStages = trade.exitPlan.takeProfitStages; final primaryTp = trade.primaryTakeProfit; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Drift Radar Status if (trade.isActive) ...[ _buildDriftRadarCard(trade), const SizedBox(height: 14), ], // Metrics Grid Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white.withValues(alpha: 0.08)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _metricItem( trade.isActive || trade.isClosed ? 'Einstiegskurs' : 'Ziel-Einstieg', trade.averageBuyIn > 0 ? '€${_fmt(trade.averageBuyIn)}' : '–', Colors.white, ), _metricItem('Live-Kurs', currPrice > 0 ? '€${_fmt(currPrice)}' : '–', AppTheme.accentCyan), _metricItem( trade.driftStatus == DriftStatus.trailingActive ? 'Stop (Trailing)' : 'Stop-Loss', '€${_fmt(trade.currentStopLoss)}', AppTheme.accentRed, ), _metricItem( tpStages.length > 1 ? 'TP (1. Stufe)' : 'Take-Profit', primaryTp != null ? '€${_fmt(primaryTp)}' : 'Trailing-Exit', AppTheme.primaryEmerald, ), ], ), ), if (tpStages.length > 1) ...[ const SizedBox(height: 10), Row( children: [ Text( 'Alle Gewinn-Ziele: ', style: TextStyle(color: AppTheme.textMuted, fontSize: 12, fontWeight: FontWeight.bold), ), Expanded( child: Wrap( spacing: 6, runSpacing: 4, children: tpStages.map((stage) { final isCurrent = primaryTp != null && (primaryTp - stage.targetPrice).abs() < 0.01; return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: isCurrent ? AppTheme.primaryEmerald.withValues(alpha: 0.2) : Colors.white.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(6), border: Border.all( color: isCurrent ? AppTheme.primaryEmerald : Colors.white.withValues(alpha: 0.15), ), ), child: Text( 'TP${stage.stageNumber}: €${_fmt(stage.targetPrice)}', style: TextStyle( color: isCurrent ? AppTheme.primaryEmerald : Colors.white70, fontSize: 11, fontWeight: isCurrent ? FontWeight.w900 : FontWeight.bold, ), ), ); }).toList(), ), ), ], ), ], if (trade.isActive || trade.isClosed) ...[ const SizedBox(height: 14), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: pnlColor.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(12), border: Border.all(color: pnlColor.withValues(alpha: 0.3)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(trade.isClosed ? 'Realisierter PnL:' : 'Aktueller PnL:', style: const TextStyle(color: Colors.white70, fontSize: 13)), Text( '${isPnlPos ? '+' : ''}€${pnlAbs.abs().toStringAsFixed(2)} (${isPnlPos ? '+' : ''}${pnlPct.toStringAsFixed(2)}%)', style: TextStyle(color: pnlColor, fontWeight: FontWeight.bold, fontSize: 15), ), ], ), ), ], const SizedBox(height: 20), // LIVE-KALKULATION (AUTOMATISCH) & MEHRSTUFIGE TP-ZIELE TradeCalculationCard(trade: trade), const SizedBox(height: 18), // Trade Parameters & Instrument _sectionTitle(Icons.tune, 'Trade-Parameter & Instrument', Colors.white70), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.02), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withValues(alpha: 0.06)), ), child: Column( children: [ _paramRow('Instrument Typ:', trade.instrumentType.label), if (trade.derivativeIsin != null && trade.derivativeIsin!.isNotEmpty) _paramRow('Derivat ISIN:', trade.derivativeIsin!), _paramRow('Ausführungsart:', trade.executionMode.label), _paramRow('Exit-Strategie:', trade.exitPlan.strategyType.label), _paramRow('Eröffnet am:', _formatDate(trade.openedAtUtc)), if (trade.closedAtUtc != null) _paramRow('Geschlossen am:', _formatDate(trade.closedAtUtc!)), ], ), ), // Execution history (replaces the removed AI-Guardian hourly // check-in timeline, which no backend DTO produces anymore — this is // the trade's real fill history instead). if (trade.fills.isNotEmpty) ...[ const SizedBox(height: 18), _sectionTitle(Icons.history_toggle_off, 'Ausführungshistorie (${trade.fills.length} Fills)', AppTheme.accentCyan), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white.withValues(alpha: 0.06)), ), child: Column( children: trade.fills.reversed.map((f) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _formatDate(f.executedAtUtc), style: TextStyle(color: AppTheme.textMuted, fontSize: 11, fontWeight: FontWeight.bold), ), const SizedBox(width: 10), Expanded( child: Text( '${_fmt(f.quantity)} Stk. @ €${_fmt(f.price)}${f.fee > 0 ? ' • Gebühr €${_fmt(f.fee)}' : ''}${f.note != null && f.note!.isNotEmpty ? ' • ${f.note}' : ''}', style: const TextStyle(color: Colors.white70, fontSize: 12), ), ), ], ), ); }).toList(), ), ), ], ], ); } String _formatDate(DateTime d) => '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year} ${d.hour.toString().padLeft(2, '0')}:${d.minute.toString().padLeft(2, '0')}'; Widget _buildDriftRadarCard(TradeModel t) { Color col; String title; String desc; switch (t.driftStatus) { case DriftStatus.trailingActive: col = AppTheme.accentCyan; title = 'Trailing Stop aktiv nachgezogen'; desc = 'Der aktuelle Stop-Loss wurde zur Absicherung von Gewinnen nachgezogen.'; break; case DriftStatus.driftWarning: col = Colors.orangeAccent; title = 'Leichte Drift / Kursabweichung'; desc = 'Der unrealisierte Verlust hat die Warnschwelle überschritten.'; break; case DriftStatus.onTrack: col = AppTheme.primaryEmerald; title = 'Auf Kurs'; desc = 'Keine besonderen Abweichungen erkannt.'; break; } return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: col.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: col.withValues(alpha: 0.4)), ), child: Row( children: [ Icon(Icons.radar, color: col, size: 22), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Drift-Radar: $title', style: TextStyle(color: col, fontWeight: FontWeight.bold, fontSize: 13)), Text(desc, style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), ], ), ), ], ), ); } Widget _sectionTitle(IconData icon, String title, Color color) { return Row( children: [ Icon(icon, size: 16, color: color), const SizedBox(width: 8), Text(title, style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 13)), ], ); } Widget _metricItem(String label, String value, Color color) { return Column( children: [ Text(label, style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), const SizedBox(height: 4), Text(value, style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 13)), ], ); } Widget _paramRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle(color: AppTheme.textMuted, fontSize: 12)), Text(value, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12)), ], ), ); } }