Files
TravelMate/lib/components/account/group_expenses_page.dart

264 lines
7.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../blocs/expense/expense_bloc.dart';
import '../../blocs/expense/expense_event.dart';
import '../../blocs/expense/expense_state.dart';
import '../../blocs/balance/balance_bloc.dart';
import '../../blocs/balance/balance_event.dart';
import '../../blocs/balance/balance_state.dart';
import '../../blocs/user/user_bloc.dart';
import '../../blocs/user/user_state.dart' as user_state;
import '../../models/account.dart';
import '../../models/group.dart';
import 'add_expense_dialog.dart';
import 'balances_tab.dart';
import 'expenses_tab.dart';
import 'settlements_tab.dart';
class GroupExpensesPage extends StatefulWidget {
final Account account;
final Group group;
const GroupExpensesPage({
super.key,
required this.account,
required this.group,
});
@override
State<GroupExpensesPage> createState() => _GroupExpensesPageState();
}
class _GroupExpensesPageState extends State<GroupExpensesPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_loadData();
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
void _loadData() {
// Charger les dépenses du groupe
context.read<ExpenseBloc>().add(LoadExpensesByGroup(widget.group.id));
// Charger les balances du groupe
context.read<BalanceBloc>().add(LoadGroupBalances(widget.group.id));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.account.name),
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
elevation: 0,
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
tabs: const [
Tab(
icon: Icon(Icons.balance),
text: 'Balances',
),
Tab(
icon: Icon(Icons.receipt_long),
text: 'Dépenses',
),
Tab(
icon: Icon(Icons.payment),
text: 'Règlements',
),
],
),
),
body: MultiBlocListener(
listeners: [
BlocListener<ExpenseBloc, ExpenseState>(
listener: (context, state) {
if (state is ExpenseOperationSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: Colors.green,
),
);
_loadData(); // Recharger les données après une opération
} else if (state is ExpenseError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: Colors.red,
),
);
}
},
),
],
child: TabBarView(
controller: _tabController,
children: [
// Onglet Balances
BlocBuilder<BalanceBloc, BalanceState>(
builder: (context, state) {
if (state is BalanceLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is GroupBalancesLoaded) {
return BalancesTab(balances: state.balances);
} else if (state is BalanceError) {
return _buildErrorState('Erreur lors du chargement des balances: ${state.message}');
}
return _buildEmptyState('Aucune balance disponible');
},
),
// Onglet Dépenses
BlocBuilder<ExpenseBloc, ExpenseState>(
builder: (context, state) {
if (state is ExpenseLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is ExpensesLoaded) {
return ExpensesTab(
expenses: state.expenses,
group: widget.group,
);
} else if (state is ExpenseError) {
return _buildErrorState('Erreur lors du chargement des dépenses: ${state.message}');
}
return _buildEmptyState('Aucune dépense trouvée');
},
),
// Onglet Règlements
BlocBuilder<BalanceBloc, BalanceState>(
builder: (context, state) {
if (state is BalanceLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is GroupBalancesLoaded) {
return SettlementsTab(settlements: state.settlements);
} else if (state is BalanceError) {
return _buildErrorState('Erreur lors du chargement des règlements: ${state.message}');
}
return _buildEmptyState('Aucun règlement nécessaire');
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _showAddExpenseDialog,
heroTag: "add_expense_fab",
tooltip: 'Ajouter une dépense',
child: const Icon(Icons.add),
),
);
}
Widget _buildErrorState(String message) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 80,
color: Colors.red[300],
),
const SizedBox(height: 16),
Text(
'Erreur',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.red[600],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Text(
message,
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 32),
ElevatedButton.icon(
onPressed: _loadData,
icon: const Icon(Icons.refresh),
label: const Text('Réessayer'),
),
],
),
);
}
Widget _buildEmptyState(String message) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.info_outline,
size: 80,
color: Colors.grey[400],
),
const SizedBox(height: 16),
Text(
'Aucune donnée',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
const SizedBox(height: 8),
Text(
message,
style: TextStyle(
fontSize: 16,
color: Colors.grey[500],
),
textAlign: TextAlign.center,
),
],
),
);
}
void _showAddExpenseDialog() {
final userState = context.read<UserBloc>().state;
if (userState is user_state.UserLoaded) {
showDialog(
context: context,
builder: (context) => AddExpenseDialog(
group: widget.group,
currentUser: userState.user,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Erreur: utilisateur non connecté'),
backgroundColor: Colors.red,
),
);
}
}
}