Files
2025-11-17 16:02:04 +00:00

257 lines
8.5 KiB
Dart

/// Dialogue de création de contribution
library create_contribution_dialog;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import '../../bloc/contributions_bloc.dart';
import '../../bloc/contributions_event.dart';
import '../../data/models/contribution_model.dart';
import '../../../members/bloc/membres_bloc.dart';
import '../../../members/bloc/membres_event.dart';
import '../../../members/bloc/membres_state.dart';
class CreateContributionDialog extends StatefulWidget {
const CreateContributionDialog({super.key});
@override
State<CreateContributionDialog> createState() => _CreateContributionDialogState();
}
class _CreateContributionDialogState extends State<CreateContributionDialog> {
final _formKey = GlobalKey<FormState>();
final _montantController = TextEditingController();
final _descriptionController = TextEditingController();
ContributionType _selectedType = ContributionType.mensuelle;
dynamic _selectedMembre;
DateTime _dateEcheance = DateTime.now().add(const Duration(days: 30));
bool _isLoading = false;
@override
void initState() {
super.initState();
// Charger la liste des membres
context.read<MembresBloc>().add(const LoadMembres());
}
@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: [
// Sélection du membre
BlocBuilder<MembresBloc, MembresState>(
builder: (context, state) {
if (state is MembresLoaded) {
return DropdownButtonFormField<dynamic>(
value: _selectedMembre,
decoration: const InputDecoration(
labelText: 'Membre',
border: OutlineInputBorder(),
),
items: state.membres.map((membre) {
return DropdownMenuItem(
value: membre,
child: Text('${membre.nom} ${membre.prenom}'),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedMembre = value;
});
},
validator: (value) {
if (value == null) {
return 'Veuillez sélectionner un membre';
}
return null;
},
);
}
return const CircularProgressIndicator();
},
),
const SizedBox(height: 16),
// Type de contribution
DropdownButtonFormField<ContributionType>(
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';
}
}
void _createContribution() {
if (!_formKey.currentState!.validate()) {
return;
}
if (_selectedMembre == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Veuillez sélectionner un membre'),
backgroundColor: Colors.red,
),
);
return;
}
setState(() {
_isLoading = true;
});
final contribution = ContributionModel(
membreId: _selectedMembre!.id!,
membreNom: _selectedMembre!.nom,
membrePrenom: _selectedMembre!.prenom,
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<ContributionsBloc>().add(CreateContribution(contribution: contribution));
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Contribution créée avec succès'),
backgroundColor: Colors.green,
),
);
}
}