/// Dialogue de création de contribution library create_contribution_dialog; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import '../../../../core/utils/logger.dart'; import '../../../../shared/design_system/tokens/app_colors.dart'; import 'package:intl/intl.dart'; import '../../bloc/contributions_bloc.dart'; import '../../bloc/contributions_event.dart'; import '../../data/models/contribution_model.dart'; import '../../../members/data/models/membre_complete_model.dart'; import '../../../profile/domain/repositories/profile_repository.dart'; class CreateContributionDialog extends StatefulWidget { const CreateContributionDialog({super.key}); @override State createState() => _CreateContributionDialogState(); } class _CreateContributionDialogState extends State { final _formKey = GlobalKey(); final _montantController = TextEditingController(); final _descriptionController = TextEditingController(); ContributionType _selectedType = ContributionType.mensuelle; MembreCompletModel? _me; DateTime _dateEcheance = DateTime.now().add(const Duration(days: 30)); bool _isLoading = false; bool _isInitLoading = true; @override void initState() { super.initState(); _loadMe(); } Future _loadMe() async { try { final user = await GetIt.instance().getMe(); if (mounted) { setState(() { _me = user; _isInitLoading = false; }); } } catch (e, st) { AppLogger.error('CreateContributionDialog: chargement profil échoué', error: e, stackTrace: st); if (mounted) { setState(() { _isInitLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Impossible de charger le profil. Réessayez.')), ); } } } @override void dispose() { _montantController.dispose(); _descriptionController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Nouvelle contribution'), content: SizedBox( width: MediaQuery.of(context).size.width * 0.8, child: Form( key: _formKey, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ // Utilisateur connecté if (_isInitLoading) const CircularProgressIndicator() else if (_me != null) TextFormField( initialValue: '${_me!.prenom} ${_me!.nom}', decoration: const InputDecoration( labelText: 'Membre', border: OutlineInputBorder(), prefixIcon: Icon(Icons.person), ), enabled: false, // Lecture seule ) else const Text('Impossible de récupérer votre profil', style: TextStyle(color: AppColors.error)), const SizedBox(height: 16), // Type de contribution DropdownButtonFormField( value: _selectedType, decoration: const InputDecoration( labelText: 'Type de contribution', border: OutlineInputBorder(), ), items: ContributionType.values.map((type) { return DropdownMenuItem( value: type, child: Text(_getTypeLabel(type)), ); }).toList(), onChanged: (value) { if (value != null) { setState(() { _selectedType = value; }); } }, ), const SizedBox(height: 16), // Montant TextFormField( controller: _montantController, decoration: const InputDecoration( labelText: 'Montant (FCFA)', border: OutlineInputBorder(), prefixIcon: Icon(Icons.attach_money), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return 'Veuillez saisir un montant'; } if (double.tryParse(value) == null) { return 'Montant invalide'; } return null; }, ), const SizedBox(height: 16), // Date d'échéance InkWell( onTap: () async { final date = await showDatePicker( context: context, initialDate: _dateEcheance, firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (date != null) { setState(() { _dateEcheance = date; }); } }, child: InputDecorator( decoration: const InputDecoration( labelText: 'Date d\'échéance', border: OutlineInputBorder(), prefixIcon: Icon(Icons.calendar_today), ), child: Text( DateFormat('dd/MM/yyyy').format(_dateEcheance), ), ), ), const SizedBox(height: 16), // Description TextFormField( controller: _descriptionController, decoration: const InputDecoration( labelText: 'Description (optionnel)', border: OutlineInputBorder(), prefixIcon: Icon(Icons.description), ), maxLines: 3, ), ], ), ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), ElevatedButton( onPressed: _isLoading ? null : _createContribution, child: _isLoading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Créer'), ), ], ); } String _getTypeLabel(ContributionType type) { switch (type) { case ContributionType.mensuelle: return 'Mensuelle'; case ContributionType.trimestrielle: return 'Trimestrielle'; case ContributionType.semestrielle: return 'Semestrielle'; case ContributionType.annuelle: return 'Annuelle'; case ContributionType.exceptionnelle: return 'Exceptionnelle'; } } Future _createContribution() async { if (!_formKey.currentState!.validate()) { return; } if (_me == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Profil non chargé'), backgroundColor: AppColors.error, ), ); return; } setState(() { _isLoading = true; }); final membre = _me!; String? organisationId = membre.organisationId?.trim().isNotEmpty == true ? membre.organisationId : null; String? organisationNom = membre.organisationNom; if (organisationId == null || organisationId.isEmpty) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Aucune organisation disponible. Le membre et l\'utilisateur connecté doivent être rattachés à une organisation.'), backgroundColor: AppColors.error, ), ); setState(() => _isLoading = false); return; } final contribution = ContributionModel( membreId: membre.id!, membreNom: membre.nom, membrePrenom: membre.prenom, organisationId: organisationId, organisationNom: organisationNom, type: _selectedType, annee: DateTime.now().year, montant: double.parse(_montantController.text), dateEcheance: _dateEcheance, description: _descriptionController.text.isNotEmpty ? _descriptionController.text : null, statut: ContributionStatus.nonPayee, dateCreation: DateTime.now(), dateModification: DateTime.now(), ); context.read().add(CreateContribution(contribution: contribution)); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Contribution créée avec succès'), backgroundColor: AppColors.success, ), ); } }