Refactoring
This commit is contained in:
@@ -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')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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')),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user