Files
Finlytic/FinlyticApp/lib/features/asset_detail/views/tabs/trades_tab.dart
T

247 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/widgets/glass_container.dart';
import '../../../../core/widgets/shimmer_loading.dart';
import '../../../../core/widgets/status_badge.dart';
import '../../../trades/models/trade_model.dart';
import '../../../trades/widgets/trade_execution_cockpit.dart';
import '../../../trades/widgets/trade_closing_cockpit.dart';
import '../../bloc/trades/asset_trades_bloc.dart';
import '../../bloc/trades/asset_trades_event.dart';
import '../../bloc/trades/asset_trades_state.dart';
import '../../widgets/trades/manual_analysis_dialog.dart';
import '../../widgets/trades/asset_trade_item_card.dart';
class TradesTab extends StatefulWidget {
final String symbol;
const TradesTab({super.key, required this.symbol});
@override
State<TradesTab> createState() => _TradesTabState();
}
class _TradesTabState extends State<TradesTab> {
bool _justTriggeredAnalysis = false;
@override
void initState() {
super.initState();
context.read<AssetTradesBloc>().add(LoadAssetTrades(widget.symbol));
}
void _showEditTradeExecutionDialog(BuildContext context, TradeModel trade, {bool isActive = false}) {
final tradesBloc = context.read<AssetTradesBloc>();
TradeExecutionCockpit.show(
context,
trade: trade,
defaultSymbol: widget.symbol,
isActive: isActive,
onAccept: (dto) {
tradesBloc.add(AcceptTradeEvent(dto, widget.symbol));
final tId = trade.id;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(isActive ? 'Einstellungen für Trade $tId gespeichert!' : 'Trade $tId angenommen & Position eröffnet!'),
backgroundColor: AppTheme.primaryEmerald,
behavior: SnackBarBehavior.floating,
),
);
},
onReject: (tId) {
tradesBloc.add(RejectTradeEvent(tId, widget.symbol));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Trade $tId abgelehnt.'),
backgroundColor: AppTheme.textSecondary,
behavior: SnackBarBehavior.floating,
),
);
},
);
}
@override
Widget build(BuildContext context) {
return BlocConsumer<AssetTradesBloc, AssetTradesState>(
listener: (context, state) {
if (_justTriggeredAnalysis && state is AssetTradesLoaded) {
final List<TradeModel> tradesList = state.data;
if (tradesList.isNotEmpty) {
_justTriggeredAnalysis = false;
final latestTrade = tradesList.first;
WidgetsBinding.instance.addPostFrameCallback((_) {
_showEditTradeExecutionDialog(context, latestTrade);
});
}
}
},
builder: (context, state) {
final List<TradeModel> tradesList = (state is AssetTradesLoaded) ? state.data : [];
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GlassContainer(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Trade & Signal Management', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: Colors.white)),
const SizedBox(height: 4),
Text('KI-gestützte technische & fundamentale Trade-Analyse anfordern', style: TextStyle(color: AppTheme.textMuted, fontSize: 12), maxLines: 1, overflow: TextOverflow.ellipsis),
],
),
),
const SizedBox(width: 8),
StatusBadge(label: widget.symbol, color: AppTheme.accentCyan),
],
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
ManualAnalysisDialog.show(
context,
symbol: widget.symbol,
initialRiskScore: 50.0,
onTrigger: (payload) {
setState(() => _justTriggeredAnalysis = true);
context.read<AssetTradesBloc>().add(TriggerManualAnalysis(widget.symbol, payload: payload));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('KI-Analyse für ${widget.symbol} abgeschlossen. Trade-Cockpit öffnet sich...'),
backgroundColor: AppTheme.accentCyan,
behavior: SnackBarBehavior.floating,
),
);
},
);
},
icon: const Icon(Icons.auto_awesome, size: 18),
label: const Text('KI-Analyse Starten', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.accentCyan,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
),
),
],
),
),
const SizedBox(height: 20),
if (state is AssetTradesLoading)
_buildTradesShimmer(context)
else if (state is AssetTradesError)
GlassContainer(
padding: const EdgeInsets.all(16),
child: Text('Fehler: ${state.message}', style: TextStyle(color: AppTheme.accentRed)),
)
else if (state is AssetTradesLoaded) ...[
_buildTradeList(
'Aktive Trade-Signale & Positionen',
tradesList.where((t) {
final s = t.status.toUpperCase();
return s == 'ACTIVE' || s == 'PENDING' || s == 'PROPOSED';
}).toList(),
),
const SizedBox(height: 20),
_buildTradeList(
'Historische Trades & KI-Bewertungen',
tradesList.where((t) {
final s = t.status.toUpperCase();
return s == 'CLOSED' || s == 'REJECTED' || (s != 'ACTIVE' && s != 'PENDING' && s != 'PROPOSED');
}).toList(),
),
],
],
),
);
},
);
}
Widget _buildTradesShimmer(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ShimmerLoading(width: 260, height: 20, borderRadius: 6),
const SizedBox(height: 12),
for (int i = 0; i < 3; i++) ...[
const ShimmerLoading(width: double.infinity, height: 105, borderRadius: 14),
const SizedBox(height: 12),
],
],
);
}
Widget _buildTradeList(String title, List<TradeModel> trades) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)),
const SizedBox(height: 10),
if (trades.isEmpty)
GlassContainer(
padding: const EdgeInsets.all(16),
child: Center(
child: Text('Keine Trades in dieser Kategorie vorhanden.', style: TextStyle(color: AppTheme.textMuted, fontSize: 12)),
),
)
else
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: trades.length,
itemBuilder: (context, index) {
final trade = trades[index];
final s = trade.status.toUpperCase();
final isActive = s == 'ACTIVE';
return AssetTradeItemCard(
trade: trade,
defaultSymbol: widget.symbol,
onAccept: () => _showEditTradeExecutionDialog(context, trade),
onSettings: () => _showEditTradeExecutionDialog(context, trade, isActive: true),
onClose: isActive
? () {
TradeClosingCockpit.show(
context,
trade: trade,
defaultSymbol: widget.symbol,
onClose: (dto) {
final isinVal = trade.isin.isNotEmpty ? trade.isin : widget.symbol;
context.read<AssetTradesBloc>().add(CloseTradeEvent(trade.id, isinVal, dto.userExitPrice));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Trade ${trade.id} geschlossen! Realisierter Ausstiegskurs: €${dto.userExitPrice.toStringAsFixed(2)}'),
backgroundColor: AppTheme.primaryEmerald,
behavior: SnackBarBehavior.floating,
),
);
},
);
}
: null,
);
},
),
],
);
}
}