feat: Ajout de la gestion de la quantité pour les options d'événement
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user