/// A widget that displays the account content page for the travel expense app. /// /// This component manages the display of user accounts (groups) and provides /// navigation to group expense management. It handles loading account data, /// error states, and navigation to the group expenses page. /// /// Features: /// - **Account Loading**: Automatically loads accounts for the current user /// - **Error Handling**: Displays error states with retry functionality /// - **Navigation**: Navigates to group expense management pages /// - **Real-time Updates**: Uses BLoC pattern for reactive state management /// - **Pull to Refresh**: Allows users to refresh account data /// /// Dependencies: /// - [UserBloc]: For current user state management /// - [AccountBloc]: For account data management /// - [GroupRepository]: For group data operations /// /// The component automatically loads account data when initialized and /// provides a clean interface for managing group-based expenses. library; import 'package:flutter/material.dart'; import 'package:travel_mate/blocs/user/user_bloc.dart'; import '../../models/account.dart'; import '../../blocs/account/account_bloc.dart'; import '../../blocs/account/account_event.dart'; import '../../blocs/account/account_state.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/components/error/error_content.dart'; import '../../blocs/user/user_state.dart' as user_state; import '../../repositories/group_repository.dart'; // Ajouter cet import import 'group_expenses_page.dart'; // Ajouter cet import /// Widget that displays the account content page with account management functionality. class AccountContent extends StatefulWidget { const AccountContent({super.key}); @override State createState() => _AccountContentState(); } /// State class for AccountContent that manages account loading and navigation. class _AccountContentState extends State { /// Repository for group data operations used for navigation final _groupRepository = GroupRepository(); // Ajouter cette ligne @override void initState() { super.initState(); // Load immediately without waiting for the next frame WidgetsBinding.instance.addPostFrameCallback((_) { _loadInitialData(); }); } /// Loads the initial account data for the current user. /// /// Retrieves the current user from UserBloc and loads their accounts /// using the AccountBloc. Handles errors gracefully with error display. void _loadInitialData() { try { final userState = context.read().state; if (userState is user_state.UserLoaded) { final userId = userState.user.id; context.read().add(LoadAccountsByUserId(userId)); } else { throw Exception('User not connected'); } } catch (e) { ErrorContent( message: 'Error loading accounts: $e', onRetry: () {}, ); } } /// Navigates to the group expenses page for a specific account. /// /// Retrieves the group associated with the account and navigates to /// the group expenses management page. Shows error messages if the /// group cannot be found or if navigation fails. /// /// Args: /// [account]: The account to navigate to for expense management Future _navigateToGroupExpenses(Account account) async { try { // Retrieve the group associated with the account final group = await _groupRepository.getGroupByTripId(account.tripId); if (group != null && mounted) { Navigator.push( context, MaterialPageRoute( builder: (context) => GroupExpensesPage( account: account, group: group, ), ), ); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Group not found for this account'), backgroundColor: Colors.red, ), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error loading group: $e'), backgroundColor: Colors.red, ), ); } } } /// Builds the main widget for the account content page. /// /// Creates a responsive UI that handles different user and account states: /// - Shows loading indicator for user authentication /// - Displays error content for user errors /// - Builds account content based on account loading states /// /// Returns: /// Widget representing the complete account page UI @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, userState) { if (userState is user_state.UserLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } if (userState is user_state.UserError) { return ErrorContent( message: 'User error: ${userState.message}', onRetry: () {}, ); } if (userState is! user_state.UserLoaded) { return const Scaffold( body: Center(child: Text('User not connected')), ); } final user = userState.user; return BlocConsumer( listener: (context, accountState) { if (accountState is AccountError) { ErrorContent( message: 'Account loading error: ${accountState.message}', onRetry: () { context.read().add(LoadAccountsByUserId(user.id)); }, ); } }, builder: (context, accountState) { return Scaffold( body: SafeArea(child: _buildContent(accountState, user.id)) ); }, ); }, ); } /// Builds the main content based on the current account state. /// /// Handles different account loading states and renders appropriate UI: /// - Loading: Shows circular progress indicator with loading message /// - Error: Displays error content with retry functionality /// - Loaded: Renders the accounts list or empty state message /// /// Args: /// [accountState]: Current state of account loading /// [userId]: ID of the current user for reload operations /// /// Returns: /// Widget representing the account content UI Widget _buildContent(AccountState accountState, String userId) { if (accountState is AccountLoading) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Loading accounts...'), ], ), ); } if (accountState is AccountError) { return ErrorContent( message: 'Account loading error...', onRetry: () { context.read().add(LoadAccountsByUserId(userId)); }, ); } if (accountState is AccountsLoaded) { if (accountState.accounts.isEmpty) { return _buildEmptyState(); } return _buildAccountsList(accountState.accounts, userId); } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Unknown state'), const SizedBox(height: 16), ElevatedButton( onPressed: () { context.read().add(LoadAccountsByUserId(userId)); }, child: const Text('Load accounts'), ), ], ), ); } /// Builds the empty state widget when no accounts are found. /// /// Displays a user-friendly message explaining that accounts are /// automatically created when trips are created. Shows an icon /// and informative text to guide the user. /// /// Returns: /// Widget representing the empty accounts state Widget _buildEmptyState() { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.account_balance_wallet, size: 80, color: Colors.grey), const SizedBox(height: 16), const Text( 'No accounts found', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Accounts are automatically created when you create a trip', style: TextStyle(fontSize: 14, color: Colors.grey), textAlign: TextAlign.center, ), ], ), ), ); } /// Builds a scrollable list of user accounts with pull-to-refresh functionality. /// /// Creates a RefreshIndicator-wrapped ListView that displays all user accounts /// in card format. Includes a header with title and description, and renders /// each account using the _buildSimpleAccountCard method. /// /// Args: /// [accounts]: List of accounts to display /// [userId]: Current user ID for refresh operations /// /// Returns: /// Widget containing the accounts list with pull-to-refresh capability Widget _buildAccountsList(List accounts, String userId) { return RefreshIndicator( onRefresh: () async { context.read().add(LoadAccountsByUserId(userId)); await Future.delayed(const Duration(milliseconds: 500)); }, child: ListView( padding: const EdgeInsets.all(16), children: [ const Text( 'My accounts', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( 'Manage your travel accounts', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), const SizedBox(height: 24), ...accounts.map((account) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: _buildSimpleAccountCard(account), ); }) ], ) ); } /// Builds an individual account card with account information. /// /// Creates a Material Design card displaying account details including: /// - Account name with color-coded avatar /// - Member count and member names (up to 2 displayed) /// - Navigation capability to group expenses /// - Error handling for card rendering issues /// /// Args: /// [account]: Account object containing account details /// /// Returns: /// Widget representing a single account card Widget _buildSimpleAccountCard(Account account) { try { final colors = [Colors.blue, Colors.purple, Colors.green, Colors.orange]; final color = colors[account.name.hashCode.abs() % colors.length]; String memberInfo = '${account.members.length} member${account.members.length > 1 ? 's' : ''}'; if(account.members.isNotEmpty){ final names = account.members .take(2) .map((m) => m.pseudo.isNotEmpty ? m.pseudo : m.firstName) .join(', '); memberInfo += '\n$names'; } return Card( elevation: 2, child: ListTile( leading: CircleAvatar( backgroundColor: color, child: const Icon(Icons.account_balance_wallet, color: Colors.white), ), title: Text( account.name, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(memberInfo), trailing: const Icon(Icons.chevron_right), onTap: () => _navigateToGroupExpenses(account), // Navigate to group expenses ), ); } catch (e) { return Card( color: Colors.red, child: const ListTile( leading: Icon(Icons.error, color: Colors.red), title: Text('Display error'), ) ); } } }