import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import 'package:travel_mate/models/expense_split.dart'; import '../../blocs/expense/expense_bloc.dart'; import '../../blocs/expense/expense_event.dart'; import '../../blocs/user/user_bloc.dart'; import '../../blocs/user/user_state.dart' as user_state; import '../../models/expense.dart'; import '../../models/group.dart'; import 'add_expense_dialog.dart'; class ExpenseDetailDialog extends StatelessWidget { final Expense expense; final Group group; const ExpenseDetailDialog({ super.key, required this.expense, required this.group, }); @override Widget build(BuildContext context) { final dateFormat = DateFormat('dd MMMM yyyy'); final timeFormat = DateFormat('HH:mm'); return BlocBuilder( builder: (context, userState) { final currentUser = userState is user_state.UserLoaded ? userState.user : null; final canEdit = currentUser?.id == expense.paidById; return Dialog( child: Container( constraints: const BoxConstraints(maxWidth: 500, maxHeight: 700), child: Scaffold( appBar: AppBar( title: const Text('Détails de la dépense'), automaticallyImplyLeading: false, actions: [ if (canEdit) ...[ IconButton( icon: const Icon(Icons.edit), onPressed: () { Navigator.of(context).pop(); _showEditDialog(context, currentUser!); }, ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => _confirmDelete(context), ), ], IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), ], ), body: ListView( padding: const EdgeInsets.all(16), children: [ // En-tête avec icône Center( child: Column( children: [ Container( width: 80, height: 80, decoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Icon( expense.category.icon, size: 40, color: Colors.blue, ), ), const SizedBox(height: 16), Text( expense.description, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( expense.category.displayName, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ), const SizedBox(height: 24), // Montant Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Text( '${expense.amount.toStringAsFixed(2)} ${expense.currency.symbol}', style: const TextStyle( fontSize: 32, fontWeight: FontWeight.bold, color: Colors.green, ), ), if (expense.currency != ExpenseCurrency.eur) Text( '≈ ${expense.amountInEur.toStringAsFixed(2)} €', style: TextStyle( fontSize: 16, color: Colors.grey[600], ), ), ], ), ), ), const SizedBox(height: 16), // Informations _buildInfoRow(Icons.person, 'Payé par', expense.paidByName), _buildInfoRow(Icons.calendar_today, 'Date', dateFormat.format(expense.date)), _buildInfoRow(Icons.access_time, 'Heure', timeFormat.format(expense.createdAt)), if (expense.isEdited && expense.editedAt != null) _buildInfoRow( Icons.edit, 'Modifié le', dateFormat.format(expense.editedAt!), ), const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), // Divisions const Text( 'Répartition', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), ...expense.splits.map((split) => _buildSplitTile(context, split)), const SizedBox(height: 16), // Reçu if (expense.receiptUrl != null) ...[ const Divider(), const SizedBox(height: 8), const Text( 'Reçu', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( expense.receiptUrl!, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return const Center(child: CircularProgressIndicator()); }, errorBuilder: (context, error, stackTrace) { return const Center( child: Text('Erreur de chargement de l\'image'), ); }, ), ), ], const SizedBox(height: 16), // Bouton archiver if (!expense.isArchived && canEdit) OutlinedButton.icon( onPressed: () => _confirmArchive(context), icon: const Icon(Icons.archive), label: const Text('Archiver cette dépense'), ), ], ), ), ), ); }, ); } Widget _buildInfoRow(IconData icon, String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ Icon(icon, size: 20, color: Colors.grey[600]), const SizedBox(width: 12), Text( label, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), const Spacer(), Text( value, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildSplitTile(BuildContext context, ExpenseSplit split) { return BlocBuilder( builder: (context, userState) { final currentUser = userState is user_state.UserLoaded ? userState.user : null; final isCurrentUser = currentUser?.id == split.userId; return ListTile( leading: CircleAvatar( backgroundColor: split.isPaid ? Colors.green : Colors.orange, child: Icon( split.isPaid ? Icons.check : Icons.pending, color: Colors.white, size: 20, ), ), title: Text( split.userName, style: TextStyle( fontWeight: isCurrentUser ? FontWeight.bold : FontWeight.normal, ), ), subtitle: Text(split.isPaid ? 'Payé' : 'En attente'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '${split.amount.toStringAsFixed(2)} €', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), if (!split.isPaid && isCurrentUser) ...[ const SizedBox(width: 8), IconButton( icon: const Icon(Icons.check_circle, color: Colors.green), onPressed: () { context.read().add(MarkSplitAsPaid( expenseId: expense.id, userId: split.userId, )); Navigator.of(context).pop(); }, ), ], ], ), ); }, ); } void _showEditDialog(BuildContext context, user_state.UserModel currentUser) { showDialog( context: context, builder: (dialogContext) => BlocProvider.value( value: context.read(), child: AddExpenseDialog( group: group, currentUser: currentUser, expenseToEdit: expense, ), ), ); } void _confirmDelete(BuildContext context) { showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Supprimer la dépense'), content: const Text('Êtes-vous sûr de vouloir supprimer cette dépense ?'), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Annuler'), ), TextButton( onPressed: () { context.read().add(DeleteExpense( expense.id, )); Navigator.of(dialogContext).pop(); Navigator.of(context).pop(); }, style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Supprimer'), ), ], ), ); } void _confirmArchive(BuildContext context) { showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Archiver la dépense'), content: const Text('Cette dépense sera archivée et n\'apparaîtra plus dans les calculs de balance.'), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Annuler'), ), TextButton( onPressed: () { context.read().add(ArchiveExpense( expense.id, )); Navigator.of(dialogContext).pop(); Navigator.of(context).pop(); }, child: const Text('Archiver'), ), ], ), ); } }