feat: Ajout de la gestion de la quantité pour les options d'événement

This commit is contained in:
ElPoyo
2025-12-16 19:23:48 +01:00
parent 08f046c89c
commit 28d9e008af
6 changed files with 240 additions and 28 deletions

View File

@@ -149,8 +149,65 @@ class _OptionSelectorWidgetState extends State<OptionSelectorWidget> {
child: Text(opt['details'],
style: const TextStyle(fontSize: 13)),
),
Text('Prix : ${opt['price'] ?? ''}',
style: const TextStyle(fontSize: 13)),
Row(
children: [
Text('Prix unitaire : ${opt['price'] ?? ''}',
style: const TextStyle(fontSize: 13)),
if (opt['isQuantitative'] == true && opt['quantity'] != null && opt['quantity'] > 1) ...[
const Text(' × ', style: TextStyle(fontSize: 13)),
Text('${opt['quantity']}',
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
Text(' = ${((opt['price'] ?? 0) * (opt['quantity'] ?? 1)).toStringAsFixed(2)}',
style: const TextStyle(fontSize: 13, color: Colors.green, fontWeight: FontWeight.bold)),
],
],
),
if (opt['isQuantitative'] == true)
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Row(
children: [
const Text('Quantité : ', style: TextStyle(fontSize: 12)),
IconButton(
icon: const Icon(Icons.remove_circle_outline, size: 20),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () {
final currentQty = opt['quantity'] ?? 1;
if (currentQty > 1) {
final newList = List<Map<String, dynamic>>.from(widget.selectedOptions);
final index = newList.indexWhere((o) => o['id'] == opt['id']);
if (index != -1) {
newList[index] = {...newList[index], 'quantity': currentQty - 1};
widget.onChanged(newList);
setState(() => _rebuildKey++);
}
}
},
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text('${opt['quantity'] ?? 1}',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
),
IconButton(
icon: const Icon(Icons.add_circle_outline, size: 20),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () {
final currentQty = opt['quantity'] ?? 1;
final newList = List<Map<String, dynamic>>.from(widget.selectedOptions);
final index = newList.indexWhere((o) => o['id'] == opt['id']);
if (index != -1) {
newList[index] = {...newList[index], 'quantity': currentQty + 1};
widget.onChanged(newList);
setState(() => _rebuildKey++);
}
},
),
],
),
),
],
),
),
@@ -224,6 +281,8 @@ class _OptionSelectorWidgetState extends State<OptionSelectorWidget> {
: firestoreData['name'], // Affichage avec code
'details': firestoreData['details'] ?? '',
'price': optionData['price'],
'quantity': optionData['quantity'] ?? 1,
'isQuantitative': firestoreData['isQuantitative'] ?? false,
});
} else {
enrichedOptions.add({
@@ -231,11 +290,17 @@ class _OptionSelectorWidgetState extends State<OptionSelectorWidget> {
'name': 'Option supprimée (${optionData['id']})',
'details': 'Cette option n\'existe plus',
'price': optionData['price'],
'quantity': optionData['quantity'] ?? 1,
'isQuantitative': false,
});
}
} else {
// Ancien format, utiliser les données locales
enrichedOptions.add(optionData);
enrichedOptions.add({
...optionData,
'quantity': optionData['quantity'] ?? 1,
'isQuantitative': optionData['isQuantitative'] ?? false,
});
}
} catch (e) {
// En cas d'erreur, utiliser les données disponibles
@@ -244,6 +309,8 @@ class _OptionSelectorWidgetState extends State<OptionSelectorWidget> {
'name': optionData['name'] ?? 'Erreur de chargement',
'details': 'Impossible de charger les détails',
'price': optionData['price'],
'quantity': optionData['quantity'] ?? 1,
'isQuantitative': false,
});
}
}
@@ -354,27 +421,46 @@ class _OptionPickerDialogState extends State<_OptionPickerDialog> {
itemBuilder: (context, i) {
final opt = filtered[i];
return ListTile(
title: Text('${opt.code} - ${opt.name}'), // Affichage avec code
title: Text('${opt.code} - ${opt.name}${opt.isQuantitative ? ' (Quantitatif)' : ''}'), // Affichage avec code
subtitle: Text('${opt.details}\nFourchette: ${opt.valMin}€ ~ ${opt.valMax}'),
onTap: () async {
final min = opt.valMin;
final max = opt.valMax;
final defaultPrice =
((min + max) / 2).toStringAsFixed(2);
final price = await showDialog<double>(
final result = await showDialog<Map<String, dynamic>>(
context: context,
builder: (ctx) {
final priceController =
TextEditingController(text: defaultPrice);
final quantityController =
TextEditingController(text: '1');
return AlertDialog(
title: Text('Prix pour ${opt.code} - ${opt.name}'), // Affichage avec code
content: TextField(
controller: priceController,
keyboardType:
const TextInputType.numberWithOptions(
decimal: true),
decoration: const InputDecoration(
labelText: 'Prix (€)'),
title: Text('Ajouter ${opt.code} - ${opt.name}'), // Affichage avec code
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: priceController,
keyboardType:
const TextInputType.numberWithOptions(
decimal: true),
decoration: const InputDecoration(
labelText: 'Prix unitaire (€)'),
),
if (opt.isQuantitative) ...[
const SizedBox(height: 16),
TextField(
controller: quantityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Quantité',
helperText: 'Le prix sera multiplié par la quantité',
),
),
],
],
),
actions: [
TextButton(
@@ -387,7 +473,13 @@ class _OptionPickerDialogState extends State<_OptionPickerDialog> {
priceController.text
.replaceAll(',', '.')) ??
0.0;
Navigator.pop(ctx, price);
final quantity = opt.isQuantitative
? (int.tryParse(quantityController.text) ?? 1)
: 1;
Navigator.pop(ctx, {
'price': price,
'quantity': quantity,
});
},
child: const Text('Ajouter'),
),
@@ -395,10 +487,11 @@ class _OptionPickerDialogState extends State<_OptionPickerDialog> {
);
},
);
if (price != null) {
if (result != null) {
Navigator.pop(context, {
'id': opt.id, // ID de l'option (obligatoire pour récupérer les données)
'price': price, // Prix choisi par l'utilisateur (obligatoire car personnalisé)
'price': result['price'], // Prix choisi par l'utilisateur (obligatoire car personnalisé)
'quantity': result['quantity'] ?? 1, // Quantité (par défaut 1)
});
}
},
@@ -457,6 +550,7 @@ class _CreateOptionDialogState extends State<_CreateOptionDialog> {
final _minPriceController = TextEditingController();
final _maxPriceController = TextEditingController();
final List<String> _selectedTypes = [];
bool _isQuantitative = false;
String? _error;
bool _checkingCode = false;
List<Map<String,dynamic>> _allEventTypes = [];
@@ -559,6 +653,19 @@ class _CreateOptionDialogState extends State<_CreateOptionDialog> {
],
),
const SizedBox(height: 8),
CheckboxListTile(
title: const Text('Option quantitative'),
subtitle: const Text('Permet de spécifier une quantité'),
value: _isQuantitative,
onChanged: (value) {
setState(() {
_isQuantitative = value ?? false;
});
},
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -642,6 +749,7 @@ class _CreateOptionDialogState extends State<_CreateOptionDialog> {
'valMin': min,
'valMax': max,
'eventTypes': _selectedTypes,
'isQuantitative': _isQuantitative,
});
Navigator.pop(context, true);
} catch (e) {