408 lines
12 KiB
Dart
408 lines
12 KiB
Dart
import 'package:flutter/material.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';
|
|
|
|
/// Widget de carte pour afficher une demande d'aide
|
|
///
|
|
/// Cette carte affiche les informations essentielles d'une demande d'aide
|
|
/// avec un design cohérent et des interactions tactiles.
|
|
class DemandeAideCard extends StatelessWidget {
|
|
final DemandeAide demande;
|
|
final bool isSelected;
|
|
final bool isSelectionMode;
|
|
final VoidCallback? onTap;
|
|
final VoidCallback? onLongPress;
|
|
final ValueChanged<bool>? onSelectionChanged;
|
|
|
|
const DemandeAideCard({
|
|
super.key,
|
|
required this.demande,
|
|
this.isSelected = false,
|
|
this.isSelectionMode = false,
|
|
this.onTap,
|
|
this.onLongPress,
|
|
this.onSelectionChanged,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return UnifiedCard(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
child: InkWell(
|
|
onTap: onTap,
|
|
onLongPress: onLongPress,
|
|
borderRadius: BorderRadius.circular(12),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: isSelected
|
|
? Border.all(color: AppColors.primary, width: 2)
|
|
: null,
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildHeader(),
|
|
const SizedBox(height: 12),
|
|
_buildContent(),
|
|
const SizedBox(height: 12),
|
|
_buildFooter(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHeader() {
|
|
return Row(
|
|
children: [
|
|
if (isSelectionMode) ...[
|
|
Checkbox(
|
|
value: isSelected,
|
|
onChanged: onSelectionChanged,
|
|
activeColor: AppColors.primary,
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
demande.titre,
|
|
style: AppTextStyles.titleMedium.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
_buildStatutChip(),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.person,
|
|
size: 16,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: Text(
|
|
demande.nomDemandeur,
|
|
style: AppTextStyles.bodySmall.copyWith(
|
|
color: AppColors.textSecondary,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
demande.numeroReference,
|
|
style: AppTextStyles.bodySmall.copyWith(
|
|
color: AppColors.textSecondary,
|
|
fontFamily: 'monospace',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (demande.estUrgente) ...[
|
|
const SizedBox(width: 8),
|
|
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildContent() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
demande.description,
|
|
style: AppTextStyles.bodyMedium,
|
|
maxLines: 3,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
_buildTypeAideChip(),
|
|
const SizedBox(width: 8),
|
|
_buildPrioriteChip(),
|
|
const Spacer(),
|
|
if (demande.montantDemande != null)
|
|
Text(
|
|
CurrencyFormatter.formatCFA(demande.montantDemande!),
|
|
style: AppTextStyles.titleSmall.copyWith(
|
|
color: AppColors.primary,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFooter() {
|
|
return Row(
|
|
children: [
|
|
Icon(
|
|
Icons.access_time,
|
|
size: 16,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
'Créée ${DateFormatter.formatRelative(demande.dateCreation)}',
|
|
style: AppTextStyles.bodySmall.copyWith(
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
if (demande.dateModification != demande.dateCreation) ...[
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'• Modifiée ${DateFormatter.formatRelative(demande.dateModification)}',
|
|
style: AppTextStyles.bodySmall.copyWith(
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
const Spacer(),
|
|
_buildProgressIndicator(),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildStatutChip() {
|
|
final color = _getStatutColor(demande.statut);
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Text(
|
|
demande.statut.libelle,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: color,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTypeAideChip() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surface,
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: AppColors.outline),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
_getTypeAideIcon(demande.typeAide),
|
|
size: 14,
|
|
color: AppColors.primary,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
demande.typeAide.libelle,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildPrioriteChip() {
|
|
final color = _getPrioriteColor(demande.priorite);
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
_getPrioriteIcon(demande.priorite),
|
|
size: 14,
|
|
color: color,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
demande.priorite.libelle,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: color,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildProgressIndicator() {
|
|
final progress = demande.pourcentageAvancement;
|
|
final color = _getProgressColor(progress);
|
|
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 60,
|
|
height: 4,
|
|
decoration: BoxDecoration(
|
|
color: AppColors.outline,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
child: FractionallySizedBox(
|
|
alignment: Alignment.centerLeft,
|
|
widthFactor: progress / 100,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: color,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'${progress.toInt()}%',
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: color,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
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 _getPrioriteColor(PrioriteAide priorite) {
|
|
switch (priorite) {
|
|
case PrioriteAide.basse:
|
|
return AppColors.success;
|
|
case PrioriteAide.normale:
|
|
return AppColors.info;
|
|
case PrioriteAide.haute:
|
|
return AppColors.warning;
|
|
case PrioriteAide.critique:
|
|
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;
|
|
}
|
|
|
|
IconData _getTypeAideIcon(TypeAide typeAide) {
|
|
switch (typeAide) {
|
|
case TypeAide.aideFinanciereUrgente:
|
|
return Icons.attach_money;
|
|
case TypeAide.aideFinanciereMedicale:
|
|
return Icons.medical_services;
|
|
case TypeAide.aideFinanciereEducation:
|
|
return Icons.school;
|
|
case TypeAide.aideMaterielleVetements:
|
|
return Icons.checkroom;
|
|
case TypeAide.aideMaterielleNourriture:
|
|
return Icons.restaurant;
|
|
case TypeAide.aideProfessionnelleFormation:
|
|
return Icons.work;
|
|
case TypeAide.aideSocialeAccompagnement:
|
|
return Icons.support;
|
|
case TypeAide.autre:
|
|
return Icons.help;
|
|
}
|
|
}
|
|
|
|
IconData _getPrioriteIcon(PrioriteAide priorite) {
|
|
switch (priorite) {
|
|
case PrioriteAide.basse:
|
|
return Icons.keyboard_arrow_down;
|
|
case PrioriteAide.normale:
|
|
return Icons.remove;
|
|
case PrioriteAide.haute:
|
|
return Icons.keyboard_arrow_up;
|
|
case PrioriteAide.critique:
|
|
return Icons.priority_high;
|
|
}
|
|
}
|
|
}
|