import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../../../shared/design_system/unionflow_design_system.dart'; import '../../../../shared/widgets/core_card.dart'; import '../../../../shared/widgets/info_badge.dart'; import '../../../../shared/widgets/mini_avatar.dart'; import '../../bloc/solidarity_bloc.dart'; import '../../data/models/demande_aide_model.dart'; import 'demande_aide_detail_page.dart'; import '../widgets/create_demande_aide_dialog.dart'; import '../../../authentication/presentation/bloc/auth_bloc.dart'; /// Page liste des demandes d'aide (solidarité) - Version Épurée class DemandesAidePage extends StatefulWidget { const DemandesAidePage({super.key}); @override State createState() => _DemandesAidePageState(); } class _DemandesAidePageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final _currencyFormat = NumberFormat.currency(locale: 'fr_FR', symbol: 'FCFA', decimalDigits: 0); @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); _loadTab(0); } @override void dispose() { _tabController.dispose(); super.dispose(); } void _loadTab(int index) { bool isGestionnaire = false; final authState = context.read().state; if (authState is AuthAuthenticated) { isGestionnaire = authState.effectiveRole.level >= 50; } if (isGestionnaire) { switch (index) { case 0: context.read().add(const SearchDemandesAide()); // Search sans statut = getAll break; case 1: context.read().add(const SearchDemandesAide(statut: 'EN_ATTENTE')); break; case 2: context.read().add(const SearchDemandesAide(statut: 'APPROUVEE')); break; } } else { // Normal member always fetches their own requests context.read().add(const LoadDemandesAide()); } } @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { if (state.status == SolidarityStatus.error && state.message != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message!, style: AppTypography.bodyTextSmall.copyWith(color: Colors.white)), backgroundColor: AppColors.error, ), ); } }, child: Scaffold( backgroundColor: AppColors.background, appBar: UFAppBar( title: 'SOLIDARITÉ', backgroundColor: AppColors.surface, foregroundColor: AppColors.textPrimaryLight, bottom: TabBar( controller: _tabController, onTap: _loadTab, labelColor: AppColors.primaryGreen, unselectedLabelColor: AppColors.textSecondaryLight, indicatorColor: AppColors.primaryGreen, indicatorSize: TabBarIndicatorSize.label, labelStyle: AppTypography.actionText.copyWith(fontSize: 10, fontWeight: FontWeight.bold), tabs: const [ Tab(child: Text('TOUTES')), Tab(child: Text('ATTENTE')), Tab(child: Text('APPROUVÉES')), ], ), ), body: TabBarView( controller: _tabController, children: [ _buildList(null), _buildList('EN_ATTENTE'), _buildList('APPROUVEE'), ], ), ), ); } Widget _buildList(String? statutFilter) { return BlocBuilder( buildWhen: (prev, curr) => prev.status != curr.status || prev.demandes != curr.demandes, builder: (context, state) { if (state.status == SolidarityStatus.loading && state.demandes.isEmpty) { return const Center(child: CircularProgressIndicator(strokeWidth: 2)); } var list = state.demandes; if (statutFilter != null) { list = list.where((d) => d.statut == statutFilter).toList(); } if (list.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.volunteer_activism_outlined, size: 32, color: AppColors.lightBorder), const SizedBox(height: 12), Text('Aucune demande', style: AppTypography.subtitleSmall), ], ), ); } return RefreshIndicator( onRefresh: () async => _loadTab(_tabController.index), child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), itemCount: list.length, itemBuilder: (context, index) { return _DemandeCard( demande: list[index], currencyFormat: _currencyFormat, onTap: () => _openDetail(list[index]), ); }, ), ); }, ); } void _openDetail(DemandeAideModel d) { if (d.id == null) return; Navigator.of(context).push( MaterialPageRoute( builder: (context) => BlocProvider.value( value: context.read(), child: DemandeAideDetailPage(demandeId: d.id!), ), ), ).then((_) => _loadTab(_tabController.index)); } } class _DemandeCard extends StatelessWidget { final DemandeAideModel demande; final NumberFormat currencyFormat; final VoidCallback onTap; const _DemandeCard({ required this.demande, required this.currencyFormat, required this.onTap, }); @override Widget build(BuildContext context) { return CoreCard( margin: const EdgeInsets.only(bottom: 10), onTap: onTap, padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const MiniAvatar(size: 24, fallbackText: '?'), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( demande.titre ?? 'Demande sans titre', style: AppTypography.actionText.copyWith(fontSize: 12), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( demande.numeroReference ?? demande.id?.substring(0, 8) ?? '—', style: AppTypography.subtitleSmall.copyWith(fontSize: 9), ), ], ), ), _buildStatutBadge(demande.statut), ], ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('MONTANT DEMANDÉ', style: AppTypography.subtitleSmall.copyWith(fontSize: 8, fontWeight: FontWeight.bold)), Text( currencyFormat.format(demande.montantDemande ?? 0), style: AppTypography.headerSmall.copyWith(fontSize: 13, color: AppColors.primaryGreen), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('TYPE', style: AppTypography.subtitleSmall.copyWith(fontSize: 8, fontWeight: FontWeight.bold)), Text(demande.typeLibelle, style: AppTypography.bodyTextSmall.copyWith(fontSize: 10)), ], ), ], ), ], ), ); } Widget _buildStatutBadge(String? statut) { Color color; switch (statut) { case 'APPROUVEE': color = AppColors.success; break; case 'REJETEE': color = AppColors.error; break; case 'EN_ATTENTE': case 'SOUMISE': color = AppColors.brandGreenLight; break; default: color = AppColors.textSecondaryLight; } return InfoBadge(text: statut ?? 'INCONNU', backgroundColor: color); } }