Refactoring

This commit is contained in:
DahoudG
2025-09-17 17:54:06 +00:00
parent 12d514d866
commit 63fe107f98
165 changed files with 54220 additions and 276 deletions

View File

@@ -0,0 +1,770 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/widgets/unified_page_layout.dart';
import '../../../../core/widgets/unified_card.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/theme/app_text_styles.dart';
import '../../../../core/utils/date_formatter.dart';
import '../../../../core/utils/currency_formatter.dart';
import '../../domain/entities/demande_aide.dart';
import '../bloc/demandes_aide/demandes_aide_bloc.dart';
import '../bloc/demandes_aide/demandes_aide_event.dart';
import '../bloc/demandes_aide/demandes_aide_state.dart';
import '../widgets/demande_aide_status_timeline.dart';
import '../widgets/demande_aide_evaluation_section.dart';
import '../widgets/demande_aide_documents_section.dart';
/// Page de détails d'une demande d'aide
///
/// Cette page affiche toutes les informations détaillées d'une demande d'aide
/// avec des sections organisées et des actions contextuelles.
class DemandeAideDetailsPage extends StatefulWidget {
final String demandeId;
const DemandeAideDetailsPage({
super.key,
required this.demandeId,
});
@override
State<DemandeAideDetailsPage> createState() => _DemandeAideDetailsPageState();
}
class _DemandeAideDetailsPageState extends State<DemandeAideDetailsPage> {
@override
void initState() {
super.initState();
// Charger les détails de la demande
context.read<DemandesAideBloc>().add(
ObtenirDemandeAideEvent(demandeId: widget.demandeId),
);
}
@override
Widget build(BuildContext context) {
return BlocConsumer<DemandesAideBloc, DemandesAideState>(
listener: (context, state) {
if (state is DemandesAideError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.error,
),
);
} else if (state is DemandesAideOperationSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.success,
),
);
}
},
builder: (context, state) {
if (state is DemandesAideLoading) {
return const UnifiedPageLayout(
title: 'Détails de la demande',
body: Center(child: CircularProgressIndicator()),
);
}
if (state is DemandesAideError && !state.hasCachedData) {
return UnifiedPageLayout(
title: 'Détails de la demande',
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error,
size: 64,
color: AppColors.error,
),
const SizedBox(height: 16),
Text(
state.message,
style: AppTextStyles.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
if (state.canRetry)
ElevatedButton(
onPressed: () => _rechargerDemande(),
child: const Text('Réessayer'),
),
],
),
),
);
}
// Trouver la demande dans l'état
DemandeAide? demande;
if (state is DemandesAideLoaded) {
demande = state.demandes.firstWhere(
(d) => d.id == widget.demandeId,
orElse: () => throw StateError('Demande non trouvée'),
);
}
if (demande == null) {
return const UnifiedPageLayout(
title: 'Détails de la demande',
body: Center(
child: Text('Demande d\'aide non trouvée'),
),
);
}
return UnifiedPageLayout(
title: 'Détails de la demande',
actions: _buildActions(demande),
body: RefreshIndicator(
onRefresh: () async => _rechargerDemande(),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeaderSection(demande),
const SizedBox(height: 16),
_buildInfoGeneralesSection(demande),
const SizedBox(height: 16),
_buildDescriptionSection(demande),
const SizedBox(height: 16),
_buildBeneficiaireSection(demande),
const SizedBox(height: 16),
_buildContactUrgenceSection(demande),
const SizedBox(height: 16),
_buildLocalisationSection(demande),
const SizedBox(height: 16),
DemandeAideDocumentsSection(demande: demande),
const SizedBox(height: 16),
DemandeAideStatusTimeline(demande: demande),
const SizedBox(height: 16),
if (demande.evaluations.isNotEmpty)
DemandeAideEvaluationSection(demande: demande),
const SizedBox(height: 80), // Espace pour le FAB
],
),
),
),
floatingActionButton: _buildFloatingActionButton(demande),
);
},
);
}
List<Widget> _buildActions(DemandeAide demande) {
return [
PopupMenuButton<String>(
onSelected: (value) => _onMenuSelected(value, demande),
itemBuilder: (context) => [
const PopupMenuItem(
value: 'edit',
child: ListTile(
leading: Icon(Icons.edit),
title: Text('Modifier'),
dense: true,
),
),
if (demande.statut == StatutAide.brouillon)
const PopupMenuItem(
value: 'submit',
child: ListTile(
leading: Icon(Icons.send),
title: Text('Soumettre'),
dense: true,
),
),
if (demande.statut == StatutAide.soumise)
const PopupMenuItem(
value: 'evaluate',
child: ListTile(
leading: Icon(Icons.rate_review),
title: Text('Évaluer'),
dense: true,
),
),
const PopupMenuItem(
value: 'share',
child: ListTile(
leading: Icon(Icons.share),
title: Text('Partager'),
dense: true,
),
),
const PopupMenuItem(
value: 'export',
child: ListTile(
leading: Icon(Icons.file_download),
title: Text('Exporter'),
dense: true,
),
),
if (demande.statut == StatutAide.brouillon)
const PopupMenuItem(
value: 'delete',
child: ListTile(
leading: Icon(Icons.delete, color: AppColors.error),
title: Text('Supprimer', style: TextStyle(color: AppColors.error)),
dense: true,
),
),
],
),
];
}
Widget _buildFloatingActionButton(DemandeAide demande) {
if (demande.statut == StatutAide.brouillon) {
return FloatingActionButton.extended(
onPressed: () => _soumettredemande(demande),
icon: const Icon(Icons.send),
label: const Text('Soumettre'),
backgroundColor: AppColors.primary,
);
}
if (demande.statut == StatutAide.soumise) {
return FloatingActionButton.extended(
onPressed: () => _evaluerDemande(demande),
icon: const Icon(Icons.rate_review),
label: const Text('Évaluer'),
backgroundColor: AppColors.warning,
);
}
return FloatingActionButton(
onPressed: () => _modifierDemande(demande),
child: const Icon(Icons.edit),
backgroundColor: AppColors.primary,
);
}
Widget _buildHeaderSection(DemandeAide demande) {
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
demande.titre,
style: AppTextStyles.titleLarge.copyWith(
fontWeight: FontWeight.bold,
),
),
),
_buildStatutChip(demande.statut),
],
),
const SizedBox(height: 8),
Row(
children: [
Text(
demande.numeroReference,
style: AppTextStyles.bodyMedium.copyWith(
fontFamily: 'monospace',
color: AppColors.textSecondary,
),
),
const Spacer(),
if (demande.estUrgente)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColors.error.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.priority_high,
size: 16,
color: AppColors.error,
),
const SizedBox(width: 4),
Text(
'URGENT',
style: AppTextStyles.labelSmall.copyWith(
color: AppColors.error,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const SizedBox(height: 12),
_buildProgressBar(demande),
],
),
),
);
}
Widget _buildInfoGeneralesSection(DemandeAide demande) {
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Informations générales',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildInfoRow('Type d\'aide', demande.typeAide.libelle, Icons.category),
_buildInfoRow('Priorité', demande.priorite.libelle, Icons.priority_high),
_buildInfoRow('Demandeur', demande.nomDemandeur, Icons.person),
if (demande.montantDemande != null)
_buildInfoRow(
'Montant demandé',
CurrencyFormatter.formatCFA(demande.montantDemande!),
Icons.attach_money,
),
if (demande.montantApprouve != null)
_buildInfoRow(
'Montant approuvé',
CurrencyFormatter.formatCFA(demande.montantApprouve!),
Icons.check_circle,
),
_buildInfoRow(
'Date de création',
DateFormatter.formatComplete(demande.dateCreation),
Icons.calendar_today,
),
if (demande.dateModification != demande.dateCreation)
_buildInfoRow(
'Dernière modification',
DateFormatter.formatComplete(demande.dateModification),
Icons.update,
),
if (demande.dateEcheance != null)
_buildInfoRow(
'Date d\'échéance',
DateFormatter.formatComplete(demande.dateEcheance!),
Icons.schedule,
),
],
),
),
);
}
Widget _buildDescriptionSection(DemandeAide demande) {
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Description',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
demande.description,
style: AppTextStyles.bodyMedium,
),
if (demande.justification != null) ...[
const SizedBox(height: 16),
Text(
'Justification',
style: AppTextStyles.titleSmall.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Text(
demande.justification!,
style: AppTextStyles.bodyMedium,
),
],
],
),
),
);
}
Widget _buildBeneficiaireSection(DemandeAide demande) {
if (demande.beneficiaires.isEmpty) return const SizedBox.shrink();
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Bénéficiaires',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
...demande.beneficiaires.map((beneficiaire) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Icon(
Icons.person,
size: 20,
color: AppColors.primary,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${beneficiaire.prenom} ${beneficiaire.nom}',
style: AppTextStyles.bodyMedium.copyWith(
fontWeight: FontWeight.w600,
),
),
if (beneficiaire.age != null)
Text(
'${beneficiaire.age} ans',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textSecondary,
),
),
],
),
),
],
),
)),
],
),
),
);
}
Widget _buildContactUrgenceSection(DemandeAide demande) {
if (demande.contactUrgence == null) return const SizedBox.shrink();
final contact = demande.contactUrgence!;
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Contact d\'urgence',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildInfoRow('Nom', '${contact.prenom} ${contact.nom}', Icons.person),
_buildInfoRow('Téléphone', contact.telephone, Icons.phone),
if (contact.email != null)
_buildInfoRow('Email', contact.email!, Icons.email),
_buildInfoRow('Relation', contact.relation, Icons.family_restroom),
],
),
),
);
}
Widget _buildLocalisationSection(DemandeAide demande) {
if (demande.localisation == null) return const SizedBox.shrink();
final localisation = demande.localisation!;
return UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Localisation',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const Spacer(),
IconButton(
onPressed: () => _ouvrirCarte(localisation),
icon: const Icon(Icons.map),
tooltip: 'Voir sur la carte',
),
],
),
const SizedBox(height: 12),
_buildInfoRow('Adresse', localisation.adresse, Icons.location_on),
if (localisation.ville != null)
_buildInfoRow('Ville', localisation.ville!, Icons.location_city),
if (localisation.codePostal != null)
_buildInfoRow('Code postal', localisation.codePostal!, Icons.markunread_mailbox),
if (localisation.pays != null)
_buildInfoRow('Pays', localisation.pays!, Icons.flag),
],
),
),
);
}
Widget _buildInfoRow(String label, String value, IconData icon) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
icon,
size: 20,
color: AppColors.primary,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
value,
style: AppTextStyles.bodyMedium,
),
],
),
),
],
),
);
}
Widget _buildStatutChip(StatutAide statut) {
final color = _getStatutColor(statut);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
),
child: Text(
statut.libelle,
style: AppTextStyles.labelMedium.copyWith(
color: color,
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildProgressBar(DemandeAide demande) {
final progress = demande.pourcentageAvancement;
final color = _getProgressColor(progress);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Avancement',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
),
),
Text(
'${progress.toInt()}%',
style: AppTextStyles.bodySmall.copyWith(
color: color,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: 8),
LinearProgressIndicator(
value: progress / 100,
backgroundColor: AppColors.outline,
valueColor: AlwaysStoppedAnimation<Color>(color),
),
],
);
}
Color _getStatutColor(StatutAide statut) {
switch (statut) {
case StatutAide.brouillon:
return AppColors.textSecondary;
case StatutAide.soumise:
return AppColors.warning;
case StatutAide.enEvaluation:
return AppColors.info;
case StatutAide.approuvee:
return AppColors.success;
case StatutAide.rejetee:
return AppColors.error;
case StatutAide.enCours:
return AppColors.primary;
case StatutAide.terminee:
return AppColors.success;
case StatutAide.versee:
return AppColors.success;
case StatutAide.livree:
return AppColors.success;
case StatutAide.annulee:
return AppColors.error;
}
}
Color _getProgressColor(double progress) {
if (progress < 25) return AppColors.error;
if (progress < 50) return AppColors.warning;
if (progress < 75) return AppColors.info;
return AppColors.success;
}
void _rechargerDemande() {
context.read<DemandesAideBloc>().add(
ObtenirDemandeAideEvent(demandeId: widget.demandeId),
);
}
void _onMenuSelected(String value, DemandeAide demande) {
switch (value) {
case 'edit':
_modifierDemande(demande);
break;
case 'submit':
_soumettredemande(demande);
break;
case 'evaluate':
_evaluerDemande(demande);
break;
case 'share':
_partagerDemande(demande);
break;
case 'export':
_exporterDemande(demande);
break;
case 'delete':
_supprimerDemande(demande);
break;
}
}
void _modifierDemande(DemandeAide demande) {
Navigator.pushNamed(
context,
'/solidarite/demandes/modifier',
arguments: demande,
);
}
void _soumettredemande(DemandeAide demande) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Soumettre la demande'),
content: const Text(
'Êtes-vous sûr de vouloir soumettre cette demande d\'aide ? '
'Une fois soumise, elle ne pourra plus être modifiée.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
context.read<DemandesAideBloc>().add(
SoumettreDemandeAideEvent(demandeId: demande.id),
);
},
child: const Text('Soumettre'),
),
],
),
);
}
void _evaluerDemande(DemandeAide demande) {
Navigator.pushNamed(
context,
'/solidarite/demandes/evaluer',
arguments: demande,
);
}
void _partagerDemande(DemandeAide demande) {
// Implémenter le partage
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Fonctionnalité de partage à implémenter')),
);
}
void _exporterDemande(DemandeAide demande) {
// Implémenter l'export
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Fonctionnalité d\'export à implémenter')),
);
}
void _supprimerDemande(DemandeAide demande) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Supprimer la demande'),
content: const Text(
'Êtes-vous sûr de vouloir supprimer cette demande d\'aide ? '
'Cette action est irréversible.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
Navigator.pop(context); // Retour à la liste
context.read<DemandesAideBloc>().add(
SupprimerDemandesSelectionnees(demandeIds: [demande.id]),
);
},
style: ElevatedButton.styleFrom(backgroundColor: AppColors.error),
child: const Text('Supprimer'),
),
],
),
);
}
void _ouvrirCarte(Localisation localisation) {
// Implémenter l'ouverture de la carte
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ouverture de la carte à implémenter')),
);
}
}

View File

@@ -0,0 +1,601 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/widgets/unified_page_layout.dart';
import '../../../../core/widgets/unified_card.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/theme/app_text_styles.dart';
import '../../../../core/utils/validators.dart';
import '../../domain/entities/demande_aide.dart';
import '../bloc/demandes_aide/demandes_aide_bloc.dart';
import '../bloc/demandes_aide/demandes_aide_event.dart';
import '../bloc/demandes_aide/demandes_aide_state.dart';
import '../widgets/demande_aide_form_sections.dart';
/// Page de formulaire pour créer ou modifier une demande d'aide
///
/// Cette page utilise un formulaire multi-sections avec validation
/// pour créer ou modifier une demande d'aide.
class DemandeAideFormPage extends StatefulWidget {
final DemandeAide? demandeExistante;
final bool isModification;
const DemandeAideFormPage({
super.key,
this.demandeExistante,
this.isModification = false,
});
@override
State<DemandeAideFormPage> createState() => _DemandeAideFormPageState();
}
class _DemandeAideFormPageState extends State<DemandeAideFormPage> {
final _formKey = GlobalKey<FormState>();
final _pageController = PageController();
// Controllers pour les champs de texte
final _titreController = TextEditingController();
final _descriptionController = TextEditingController();
final _justificationController = TextEditingController();
final _montantController = TextEditingController();
// Variables d'état du formulaire
TypeAide? _typeAide;
PrioriteAide _priorite = PrioriteAide.normale;
bool _estUrgente = false;
DateTime? _dateEcheance;
List<BeneficiaireAide> _beneficiaires = [];
ContactUrgence? _contactUrgence;
Localisation? _localisation;
List<PieceJustificative> _piecesJustificatives = [];
int _currentStep = 0;
final int _totalSteps = 5;
bool _isLoading = false;
@override
void initState() {
super.initState();
_initializeForm();
}
@override
void dispose() {
_titreController.dispose();
_descriptionController.dispose();
_justificationController.dispose();
_montantController.dispose();
_pageController.dispose();
super.dispose();
}
void _initializeForm() {
if (widget.demandeExistante != null) {
final demande = widget.demandeExistante!;
_titreController.text = demande.titre;
_descriptionController.text = demande.description;
_justificationController.text = demande.justification ?? '';
_montantController.text = demande.montantDemande?.toString() ?? '';
_typeAide = demande.typeAide;
_priorite = demande.priorite;
_estUrgente = demande.estUrgente;
_dateEcheance = demande.dateEcheance;
_beneficiaires = List.from(demande.beneficiaires);
_contactUrgence = demande.contactUrgence;
_localisation = demande.localisation;
_piecesJustificatives = List.from(demande.piecesJustificatives);
}
}
@override
Widget build(BuildContext context) {
return BlocConsumer<DemandesAideBloc, DemandesAideState>(
listener: (context, state) {
if (state is DemandesAideLoading) {
setState(() {
_isLoading = true;
});
} else {
setState(() {
_isLoading = false;
});
}
if (state is DemandesAideError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.error,
),
);
} else if (state is DemandesAideOperationSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.success,
),
);
Navigator.pop(context, true);
} else if (state is DemandesAideValidation) {
if (!state.isValid) {
_showValidationErrors(state.erreurs);
}
}
},
builder: (context, state) {
return UnifiedPageLayout(
title: widget.isModification ? 'Modifier la demande' : 'Nouvelle demande',
actions: [
if (_currentStep > 0)
IconButton(
onPressed: _previousStep,
icon: const Icon(Icons.arrow_back),
tooltip: 'Étape précédente',
),
IconButton(
onPressed: _saveDraft,
icon: const Icon(Icons.save),
tooltip: 'Sauvegarder le brouillon',
),
],
body: Column(
children: [
_buildProgressIndicator(),
Expanded(
child: Form(
key: _formKey,
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildStep1InfoGenerales(),
_buildStep2Beneficiaires(),
_buildStep3Contact(),
_buildStep4Localisation(),
_buildStep5Documents(),
],
),
),
),
_buildBottomActions(),
],
),
);
},
);
}
Widget _buildProgressIndicator() {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
children: List.generate(_totalSteps, (index) {
final isActive = index == _currentStep;
final isCompleted = index < _currentStep;
return Expanded(
child: Container(
height: 4,
margin: EdgeInsets.only(right: index < _totalSteps - 1 ? 8 : 0),
decoration: BoxDecoration(
color: isCompleted || isActive
? AppColors.primary
: AppColors.outline,
borderRadius: BorderRadius.circular(2),
),
),
);
}),
),
const SizedBox(height: 8),
Text(
'Étape ${_currentStep + 1} sur $_totalSteps: ${_getStepTitle(_currentStep)}',
style: AppTextStyles.bodyMedium.copyWith(
color: AppColors.textSecondary,
),
),
],
),
);
}
Widget _buildStep1InfoGenerales() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Informations générales',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
TextFormField(
controller: _titreController,
decoration: const InputDecoration(
labelText: 'Titre de la demande *',
hintText: 'Ex: Aide pour frais médicaux',
border: OutlineInputBorder(),
),
validator: Validators.required,
maxLength: 100,
),
const SizedBox(height: 16),
DropdownButtonFormField<TypeAide>(
value: _typeAide,
decoration: const InputDecoration(
labelText: 'Type d\'aide *',
border: OutlineInputBorder(),
),
items: TypeAide.values.map((type) => DropdownMenuItem(
value: type,
child: Text(type.libelle),
)).toList(),
onChanged: (value) {
setState(() {
_typeAide = value;
});
},
validator: (value) => value == null ? 'Veuillez sélectionner un type d\'aide' : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _descriptionController,
decoration: const InputDecoration(
labelText: 'Description détaillée *',
hintText: 'Décrivez votre situation et vos besoins...',
border: OutlineInputBorder(),
),
maxLines: 4,
validator: Validators.required,
maxLength: 1000,
),
const SizedBox(height: 16),
TextFormField(
controller: _justificationController,
decoration: const InputDecoration(
labelText: 'Justification',
hintText: 'Pourquoi cette aide est-elle nécessaire ?',
border: OutlineInputBorder(),
),
maxLines: 3,
maxLength: 500,
),
],
),
),
),
const SizedBox(height: 16),
UnifiedCard(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Détails de la demande',
style: AppTextStyles.titleMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
TextFormField(
controller: _montantController,
decoration: const InputDecoration(
labelText: 'Montant demandé (FCFA)',
hintText: '0',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.attach_money),
),
keyboardType: TextInputType.number,
validator: (value) {
if (value != null && value.isNotEmpty) {
final montant = double.tryParse(value);
if (montant == null || montant <= 0) {
return 'Veuillez saisir un montant valide';
}
}
return null;
},
),
const SizedBox(height: 16),
DropdownButtonFormField<PrioriteAide>(
value: _priorite,
decoration: const InputDecoration(
labelText: 'Priorité',
border: OutlineInputBorder(),
),
items: PrioriteAide.values.map((priorite) => DropdownMenuItem(
value: priorite,
child: Text(priorite.libelle),
)).toList(),
onChanged: (value) {
setState(() {
_priorite = value ?? PrioriteAide.normale;
});
},
),
const SizedBox(height: 16),
SwitchListTile(
title: const Text('Demande urgente'),
subtitle: const Text('Cette demande nécessite un traitement prioritaire'),
value: _estUrgente,
onChanged: (value) {
setState(() {
_estUrgente = value;
});
},
),
const SizedBox(height: 16),
ListTile(
title: const Text('Date d\'échéance'),
subtitle: Text(_dateEcheance != null
? '${_dateEcheance!.day}/${_dateEcheance!.month}/${_dateEcheance!.year}'
: 'Aucune date limite'),
trailing: const Icon(Icons.calendar_today),
onTap: _selectDateEcheance,
),
],
),
),
),
],
),
);
}
Widget _buildStep2Beneficiaires() {
return DemandeAideFormBeneficiairesSection(
beneficiaires: _beneficiaires,
onBeneficiairesChanged: (beneficiaires) {
setState(() {
_beneficiaires = beneficiaires;
});
},
);
}
Widget _buildStep3Contact() {
return DemandeAideFormContactSection(
contactUrgence: _contactUrgence,
onContactChanged: (contact) {
setState(() {
_contactUrgence = contact;
});
},
);
}
Widget _buildStep4Localisation() {
return DemandeAideFormLocalisationSection(
localisation: _localisation,
onLocalisationChanged: (localisation) {
setState(() {
_localisation = localisation;
});
},
);
}
Widget _buildStep5Documents() {
return DemandeAideFormDocumentsSection(
piecesJustificatives: _piecesJustificatives,
onDocumentsChanged: (documents) {
setState(() {
_piecesJustificatives = documents;
});
},
);
}
Widget _buildBottomActions() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, -2),
),
],
),
child: Row(
children: [
if (_currentStep > 0)
Expanded(
child: OutlinedButton(
onPressed: _isLoading ? null : _previousStep,
child: const Text('Précédent'),
),
),
if (_currentStep > 0) const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: _isLoading ? null : _nextStepOrSubmit,
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(_currentStep < _totalSteps - 1 ? 'Suivant' : 'Créer la demande'),
),
),
],
),
);
}
String _getStepTitle(int step) {
switch (step) {
case 0:
return 'Informations générales';
case 1:
return 'Bénéficiaires';
case 2:
return 'Contact d\'urgence';
case 3:
return 'Localisation';
case 4:
return 'Documents';
default:
return '';
}
}
void _previousStep() {
if (_currentStep > 0) {
setState(() {
_currentStep--;
});
_pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
void _nextStepOrSubmit() {
if (_validateCurrentStep()) {
if (_currentStep < _totalSteps - 1) {
setState(() {
_currentStep++;
});
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
} else {
_submitForm();
}
}
}
bool _validateCurrentStep() {
switch (_currentStep) {
case 0:
return _formKey.currentState?.validate() ?? false;
case 1:
// Validation des bénéficiaires (optionnel)
return true;
case 2:
// Validation du contact d'urgence (optionnel)
return true;
case 3:
// Validation de la localisation (optionnel)
return true;
case 4:
// Validation des documents (optionnel)
return true;
default:
return true;
}
}
void _submitForm() {
if (!_formKey.currentState!.validate()) {
return;
}
final demande = DemandeAide(
id: widget.demandeExistante?.id ?? '',
numeroReference: widget.demandeExistante?.numeroReference ?? '',
titre: _titreController.text,
description: _descriptionController.text,
justification: _justificationController.text.isEmpty ? null : _justificationController.text,
typeAide: _typeAide!,
statut: widget.demandeExistante?.statut ?? StatutAide.brouillon,
priorite: _priorite,
estUrgente: _estUrgente,
montantDemande: _montantController.text.isEmpty ? null : double.tryParse(_montantController.text),
montantApprouve: widget.demandeExistante?.montantApprouve,
dateCreation: widget.demandeExistante?.dateCreation ?? DateTime.now(),
dateModification: DateTime.now(),
dateEcheance: _dateEcheance,
organisationId: widget.demandeExistante?.organisationId ?? '',
demandeurId: widget.demandeExistante?.demandeurId ?? '',
nomDemandeur: widget.demandeExistante?.nomDemandeur ?? '',
emailDemandeur: widget.demandeExistante?.emailDemandeur ?? '',
telephoneDemandeur: widget.demandeExistante?.telephoneDemandeur ?? '',
beneficiaires: _beneficiaires,
contactUrgence: _contactUrgence,
localisation: _localisation,
piecesJustificatives: _piecesJustificatives,
evaluations: widget.demandeExistante?.evaluations ?? [],
commentairesInternes: widget.demandeExistante?.commentairesInternes ?? [],
historiqueStatuts: widget.demandeExistante?.historiqueStatuts ?? [],
tags: widget.demandeExistante?.tags ?? [],
metadonnees: widget.demandeExistante?.metadonnees ?? {},
);
if (widget.isModification) {
context.read<DemandesAideBloc>().add(
MettreAJourDemandeAideEvent(demande: demande),
);
} else {
context.read<DemandesAideBloc>().add(
CreerDemandeAideEvent(demande: demande),
);
}
}
void _saveDraft() {
// Sauvegarder le brouillon
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Brouillon sauvegardé'),
backgroundColor: AppColors.success,
),
);
}
void _selectDateEcheance() async {
final date = await showDatePicker(
context: context,
initialDate: _dateEcheance ?? DateTime.now().add(const Duration(days: 30)),
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
if (date != null) {
setState(() {
_dateEcheance = date;
});
}
}
void _showValidationErrors(Map<String, String> erreurs) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Erreurs de validation'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: erreurs.entries.map((entry) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text('${entry.value}'),
)).toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
}

View File

@@ -0,0 +1,676 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/widgets/unified_page_layout.dart';
import '../../../../core/widgets/unified_card.dart';
import '../../../../core/widgets/unified_list_widget.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/theme/app_text_styles.dart';
import '../../../../core/utils/date_formatter.dart';
import '../../../../core/utils/currency_formatter.dart';
import '../../domain/entities/demande_aide.dart';
import '../bloc/demandes_aide/demandes_aide_bloc.dart';
import '../bloc/demandes_aide/demandes_aide_event.dart';
import '../bloc/demandes_aide/demandes_aide_state.dart';
import '../widgets/demande_aide_card.dart';
import '../widgets/demandes_aide_filter_bottom_sheet.dart';
import '../widgets/demandes_aide_sort_bottom_sheet.dart';
/// Page principale pour afficher la liste des demandes d'aide
///
/// Cette page utilise le pattern BLoC pour gérer l'état et affiche
/// une liste paginée des demandes d'aide avec des fonctionnalités
/// de filtrage, tri, recherche et sélection multiple.
class DemandesAidePage extends StatefulWidget {
final String? organisationId;
final TypeAide? typeAideInitial;
final StatutAide? statutInitial;
const DemandesAidePage({
super.key,
this.organisationId,
this.typeAideInitial,
this.statutInitial,
});
@override
State<DemandesAidePage> createState() => _DemandesAidePageState();
}
class _DemandesAidePageState extends State<DemandesAidePage> {
final ScrollController _scrollController = ScrollController();
final TextEditingController _searchController = TextEditingController();
bool _isSelectionMode = false;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
// Charger les demandes d'aide au démarrage
context.read<DemandesAideBloc>().add(ChargerDemandesAideEvent(
organisationId: widget.organisationId,
typeAide: widget.typeAideInitial,
statut: widget.statutInitial,
));
}
@override
void dispose() {
_scrollController.dispose();
_searchController.dispose();
super.dispose();
}
void _onScroll() {
if (_isBottom) {
context.read<DemandesAideBloc>().add(const ChargerPlusDemandesAideEvent());
}
}
bool get _isBottom {
if (!_scrollController.hasClients) return false;
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.offset;
return currentScroll >= (maxScroll * 0.9);
}
@override
Widget build(BuildContext context) {
return BlocConsumer<DemandesAideBloc, DemandesAideState>(
listener: (context, state) {
if (state is DemandesAideError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.error,
action: state.canRetry
? SnackBarAction(
label: 'Réessayer',
textColor: Colors.white,
onPressed: () => _rafraichir(),
)
: null,
),
);
} else if (state is DemandesAideOperationSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: AppColors.success,
),
);
} else if (state is DemandesAideExported) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Fichier exporté: ${state.filePath}'),
backgroundColor: AppColors.success,
action: SnackBarAction(
label: 'Ouvrir',
textColor: Colors.white,
onPressed: () => _ouvrirFichier(state.filePath),
),
),
);
}
},
builder: (context, state) {
return UnifiedPageLayout(
title: 'Demandes d\'aide',
showBackButton: false,
actions: _buildActions(state),
floatingActionButton: _buildFloatingActionButton(),
body: Column(
children: [
_buildSearchBar(state),
_buildFilterChips(state),
Expanded(child: _buildContent(state)),
],
),
);
},
);
}
List<Widget> _buildActions(DemandesAideState state) {
final actions = <Widget>[];
if (_isSelectionMode && state is DemandesAideLoaded) {
// Actions en mode sélection
actions.addAll([
IconButton(
icon: const Icon(Icons.select_all),
onPressed: () => _toggleSelectAll(state),
tooltip: state.toutesDemandesSelectionnees
? 'Désélectionner tout'
: 'Sélectionner tout',
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: state.nombreDemandesSelectionnees > 0
? () => _supprimerSelection(state)
: null,
tooltip: 'Supprimer la sélection',
),
IconButton(
icon: const Icon(Icons.file_download),
onPressed: state.nombreDemandesSelectionnees > 0
? () => _exporterSelection(state)
: null,
tooltip: 'Exporter la sélection',
),
IconButton(
icon: const Icon(Icons.close),
onPressed: _quitterModeSelection,
tooltip: 'Quitter la sélection',
),
]);
} else {
// Actions normales
actions.addAll([
IconButton(
icon: const Icon(Icons.filter_list),
onPressed: () => _afficherFiltres(state),
tooltip: 'Filtrer',
),
IconButton(
icon: const Icon(Icons.sort),
onPressed: () => _afficherTri(state),
tooltip: 'Trier',
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
onSelected: (value) => _onMenuSelected(value, state),
itemBuilder: (context) => [
const PopupMenuItem(
value: 'refresh',
child: ListTile(
leading: Icon(Icons.refresh),
title: Text('Actualiser'),
dense: true,
),
),
const PopupMenuItem(
value: 'select',
child: ListTile(
leading: Icon(Icons.checklist),
title: Text('Sélection multiple'),
dense: true,
),
),
const PopupMenuItem(
value: 'export_all',
child: ListTile(
leading: Icon(Icons.file_download),
title: Text('Exporter tout'),
dense: true,
),
),
const PopupMenuItem(
value: 'urgentes',
child: ListTile(
leading: Icon(Icons.priority_high, color: AppColors.error),
title: Text('Demandes urgentes'),
dense: true,
),
),
],
),
]);
}
return actions;
}
Widget _buildFloatingActionButton() {
return FloatingActionButton.extended(
onPressed: _creerNouvelleDemande,
icon: const Icon(Icons.add),
label: const Text('Nouvelle demande'),
backgroundColor: AppColors.primary,
);
}
Widget _buildSearchBar(DemandesAideState state) {
return Container(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Rechercher des demandes...',
prefixIcon: const Icon(Icons.search),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
_rechercherDemandes('');
},
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
onChanged: _rechercherDemandes,
onSubmitted: _rechercherDemandes,
),
);
}
Widget _buildFilterChips(DemandesAideState state) {
if (state is! DemandesAideLoaded || !state.hasFiltres) {
return const SizedBox.shrink();
}
return Container(
height: 50,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
if (state.filtres.typeAide != null)
_buildFilterChip(
'Type: ${state.filtres.typeAide!.libelle}',
() => _supprimerFiltre('typeAide'),
),
if (state.filtres.statut != null)
_buildFilterChip(
'Statut: ${state.filtres.statut!.libelle}',
() => _supprimerFiltre('statut'),
),
if (state.filtres.priorite != null)
_buildFilterChip(
'Priorité: ${state.filtres.priorite!.libelle}',
() => _supprimerFiltre('priorite'),
),
if (state.filtres.urgente == true)
_buildFilterChip(
'Urgente',
() => _supprimerFiltre('urgente'),
),
if (state.filtres.motCle != null && state.filtres.motCle!.isNotEmpty)
_buildFilterChip(
'Recherche: "${state.filtres.motCle}"',
() => _supprimerFiltre('motCle'),
),
ActionChip(
label: const Text('Effacer tout'),
onPressed: _effacerTousFiltres,
backgroundColor: AppColors.error.withOpacity(0.1),
labelStyle: TextStyle(color: AppColors.error),
),
],
),
);
}
Widget _buildFilterChip(String label, VoidCallback onDeleted) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Chip(
label: Text(label),
onDeleted: onDeleted,
backgroundColor: AppColors.primary.withOpacity(0.1),
labelStyle: TextStyle(color: AppColors.primary),
),
);
}
Widget _buildContent(DemandesAideState state) {
if (state is DemandesAideInitial) {
return const Center(
child: Text('Appuyez sur actualiser pour charger les demandes'),
);
}
if (state is DemandesAideLoading && state.isRefreshing == false) {
return const Center(child: CircularProgressIndicator());
}
if (state is DemandesAideError && !state.hasCachedData) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
state.isNetworkError ? Icons.wifi_off : Icons.error,
size: 64,
color: AppColors.error,
),
const SizedBox(height: 16),
Text(
state.message,
style: AppTextStyles.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
if (state.canRetry)
ElevatedButton(
onPressed: _rafraichir,
child: const Text('Réessayer'),
),
],
),
);
}
if (state is DemandesAideExporting) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(value: state.progress),
const SizedBox(height: 16),
Text(
state.currentStep ?? 'Export en cours...',
style: AppTextStyles.bodyLarge,
),
const SizedBox(height: 8),
Text(
'${(state.progress * 100).toInt()}%',
style: AppTextStyles.bodyMedium,
),
],
),
);
}
if (state is DemandesAideLoaded) {
return _buildDemandesList(state);
}
return const SizedBox.shrink();
}
Widget _buildDemandesList(DemandesAideLoaded state) {
if (state.demandesFiltrees.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inbox,
size: 64,
color: AppColors.textSecondary,
),
const SizedBox(height: 16),
Text(
state.hasData
? 'Aucun résultat pour les filtres appliqués'
: 'Aucune demande d\'aide',
style: AppTextStyles.bodyLarge,
textAlign: TextAlign.center,
),
if (state.hasFiltres) ...[
const SizedBox(height: 8),
TextButton(
onPressed: _effacerTousFiltres,
child: const Text('Effacer les filtres'),
),
],
],
),
);
}
return RefreshIndicator(
onRefresh: () async => _rafraichir(),
child: UnifiedListWidget<DemandeAide>(
items: state.demandesFiltrees,
itemBuilder: (context, demande, index) => DemandeAideCard(
demande: demande,
isSelected: state.demandesSelectionnees[demande.id] == true,
isSelectionMode: _isSelectionMode,
onTap: () => _onDemandeAideTap(demande),
onLongPress: () => _onDemandeAideLongPress(demande),
onSelectionChanged: (selected) => _onDemandeAideSelectionChanged(demande.id, selected),
),
scrollController: _scrollController,
hasReachedMax: state.hasReachedMax,
isLoading: state.isLoadingMore,
emptyWidget: const SizedBox.shrink(), // Géré plus haut
),
);
}
// Méthodes d'action
void _rafraichir() {
context.read<DemandesAideBloc>().add(const RafraichirDemandesAideEvent());
}
void _rechercherDemandes(String query) {
context.read<DemandesAideBloc>().add(FiltrerDemandesAideEvent(motCle: query));
}
void _afficherFiltres(DemandesAideState state) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => DemandesAideFilterBottomSheet(
filtresActuels: state is DemandesAideLoaded ? state.filtres : const FiltresDemandesAide(),
onFiltresChanged: (filtres) {
context.read<DemandesAideBloc>().add(FiltrerDemandesAideEvent(
typeAide: filtres.typeAide,
statut: filtres.statut,
priorite: filtres.priorite,
urgente: filtres.urgente,
motCle: filtres.motCle,
));
},
),
);
}
void _afficherTri(DemandesAideState state) {
showModalBottomSheet(
context: context,
builder: (context) => DemandesAideSortBottomSheet(
critereActuel: state is DemandesAideLoaded ? state.criterieTri : null,
croissantActuel: state is DemandesAideLoaded ? state.triCroissant : true,
onTriChanged: (critere, croissant) {
context.read<DemandesAideBloc>().add(TrierDemandesAideEvent(
critere: critere,
croissant: croissant,
));
},
),
);
}
void _onMenuSelected(String value, DemandesAideState state) {
switch (value) {
case 'refresh':
_rafraichir();
break;
case 'select':
_activerModeSelection();
break;
case 'export_all':
if (state is DemandesAideLoaded) {
_exporterTout(state);
}
break;
case 'urgentes':
_afficherDemandesUrgentes();
break;
}
}
void _creerNouvelleDemande() {
Navigator.pushNamed(context, '/solidarite/demandes/creer');
}
void _onDemandeAideTap(DemandeAide demande) {
if (_isSelectionMode) {
_onDemandeAideSelectionChanged(
demande.id,
!(context.read<DemandesAideBloc>().state as DemandesAideLoaded)
.demandesSelectionnees[demande.id] == true,
);
} else {
Navigator.pushNamed(
context,
'/solidarite/demandes/details',
arguments: demande.id,
);
}
}
void _onDemandeAideLongPress(DemandeAide demande) {
if (!_isSelectionMode) {
_activerModeSelection();
_onDemandeAideSelectionChanged(demande.id, true);
}
}
void _onDemandeAideSelectionChanged(String demandeId, bool selected) {
context.read<DemandesAideBloc>().add(SelectionnerDemandeAideEvent(
demandeId: demandeId,
selectionne: selected,
));
}
void _activerModeSelection() {
setState(() {
_isSelectionMode = true;
});
}
void _quitterModeSelection() {
setState(() {
_isSelectionMode = false;
});
context.read<DemandesAideBloc>().add(const SelectionnerToutesDemandesAideEvent(selectionne: false));
}
void _toggleSelectAll(DemandesAideLoaded state) {
context.read<DemandesAideBloc>().add(SelectionnerToutesDemandesAideEvent(
selectionne: !state.toutesDemandesSelectionnees,
));
}
void _supprimerSelection(DemandesAideLoaded state) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Confirmer la suppression'),
content: Text(
'Êtes-vous sûr de vouloir supprimer ${state.nombreDemandesSelectionnees} demande(s) d\'aide ?',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
context.read<DemandesAideBloc>().add(SupprimerDemandesSelectionnees(
demandeIds: state.demandesSelectionneesIds,
));
_quitterModeSelection();
},
style: ElevatedButton.styleFrom(backgroundColor: AppColors.error),
child: const Text('Supprimer'),
),
],
),
);
}
void _exporterSelection(DemandesAideLoaded state) {
_afficherDialogueExport(state.demandesSelectionneesIds);
}
void _exporterTout(DemandesAideLoaded state) {
_afficherDialogueExport(state.demandesFiltrees.map((d) => d.id).toList());
}
void _afficherDialogueExport(List<String> demandeIds) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Exporter les demandes'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: FormatExport.values.map((format) => ListTile(
leading: Icon(_getFormatIcon(format)),
title: Text(format.libelle),
onTap: () {
Navigator.pop(context);
context.read<DemandesAideBloc>().add(ExporterDemandesAideEvent(
demandeIds: demandeIds,
format: format,
));
},
)).toList(),
),
),
);
}
IconData _getFormatIcon(FormatExport format) {
switch (format) {
case FormatExport.pdf:
return Icons.picture_as_pdf;
case FormatExport.excel:
return Icons.table_chart;
case FormatExport.csv:
return Icons.grid_on;
case FormatExport.json:
return Icons.code;
}
}
void _afficherDemandesUrgentes() {
context.read<DemandesAideBloc>().add(ChargerDemandesUrgentesEvent(
organisationId: widget.organisationId ?? '',
));
}
void _supprimerFiltre(String filtre) {
final state = context.read<DemandesAideBloc>().state;
if (state is DemandesAideLoaded) {
var nouveauxFiltres = state.filtres;
switch (filtre) {
case 'typeAide':
nouveauxFiltres = nouveauxFiltres.copyWith(typeAide: null);
break;
case 'statut':
nouveauxFiltres = nouveauxFiltres.copyWith(statut: null);
break;
case 'priorite':
nouveauxFiltres = nouveauxFiltres.copyWith(priorite: null);
break;
case 'urgente':
nouveauxFiltres = nouveauxFiltres.copyWith(urgente: null);
break;
case 'motCle':
nouveauxFiltres = nouveauxFiltres.copyWith(motCle: '');
_searchController.clear();
break;
}
context.read<DemandesAideBloc>().add(FiltrerDemandesAideEvent(
typeAide: nouveauxFiltres.typeAide,
statut: nouveauxFiltres.statut,
priorite: nouveauxFiltres.priorite,
urgente: nouveauxFiltres.urgente,
motCle: nouveauxFiltres.motCle,
));
}
}
void _effacerTousFiltres() {
_searchController.clear();
context.read<DemandesAideBloc>().add(const FiltrerDemandesAideEvent());
}
void _ouvrirFichier(String filePath) {
// Implémenter l'ouverture du fichier avec un package comme open_file
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ouverture du fichier: $filePath')),
);
}
}