import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/recipe.dart'; import '../providers/app_state.dart'; class RecipeDialog extends StatefulWidget { final Recipe? recipe; // null for add, non-null for edit const RecipeDialog({super.key, this.recipe}); @override State createState() => _RecipeDialogState(); } class _RecipeDialogState extends State { final _formKey = GlobalKey(); late final TextEditingController _nameController; late final TextEditingController _grindSizeController; late final TextEditingController _coffeeAmountController; late final TextEditingController _waterAmountController; late final TextEditingController _brewTimeController; late final TextEditingController _instructionsController; late final TextEditingController _notesController; late ServingTemp _selectedServingTemp; late MilkType? _selectedMilkType; late BrewMethod _selectedBrewMethod; @override void initState() { super.initState(); final recipe = widget.recipe; _nameController = TextEditingController(text: recipe?.name ?? ''); _grindSizeController = TextEditingController(text: recipe?.grindSize.toString() ?? ''); _coffeeAmountController = TextEditingController(text: recipe?.coffeeAmount.toString() ?? ''); _waterAmountController = TextEditingController(text: recipe?.waterAmount.toString() ?? ''); _brewTimeController = TextEditingController(text: recipe?.brewTime.toString() ?? ''); _instructionsController = TextEditingController(text: recipe?.instructions ?? ''); _notesController = TextEditingController(text: recipe?.notes ?? ''); _selectedServingTemp = recipe?.servingTemp ?? ServingTemp.hot; _selectedMilkType = recipe?.milkType; _selectedBrewMethod = recipe?.brewMethod ?? BrewMethod.espresso; } @override void dispose() { _nameController.dispose(); _grindSizeController.dispose(); _coffeeAmountController.dispose(); _waterAmountController.dispose(); _brewTimeController.dispose(); _instructionsController.dispose(); _notesController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Dialog( child: Container( width: MediaQuery.of(context).size.width > 600 ? 600 : double.infinity, height: MediaQuery.of(context).size.height * 0.9, padding: const EdgeInsets.all(24), child: Column( children: [ // Header Row( children: [ Icon( Icons.menu_book, color: Theme.of(context).colorScheme.primary, size: 28, ), const SizedBox(width: 12), Expanded( child: Text( widget.recipe == null ? 'Add New Recipe' : 'Edit Recipe', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.w600, ), ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), ], ), const Divider(), // Form Expanded( child: Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildBasicInfoSection(), const SizedBox(height: 24), _buildBrewingParametersSection(), const SizedBox(height: 24), _buildInstructionsSection(), ], ), ), ), ), // Actions const Divider(), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), const SizedBox(width: 12), ElevatedButton( onPressed: _saveRecipe, child: Text(widget.recipe == null ? 'Add Recipe' : 'Update Recipe'), ), ], ), ], ), ), ); } Widget _buildBasicInfoSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Basic Information', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Recipe Name *', hintText: 'e.g., Morning Espresso, Chemex Pour Over', border: OutlineInputBorder(), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Recipe name is required'; } return null; }, ), const SizedBox(height: 16), Row( children: [ Expanded( child: DropdownButtonFormField( value: _selectedBrewMethod, decoration: const InputDecoration( labelText: 'Brew Method', border: OutlineInputBorder(), ), items: BrewMethod.values.map((method) { return DropdownMenuItem( value: method, child: Text(_formatBrewMethod(method)), ); }).toList(), onChanged: (value) { if (value != null) { setState(() { _selectedBrewMethod = value; }); } }, ), ), const SizedBox(width: 16), Expanded( child: DropdownButtonFormField( value: _selectedServingTemp, decoration: const InputDecoration( labelText: 'Serving Temperature', border: OutlineInputBorder(), ), items: ServingTemp.values.map((temp) { return DropdownMenuItem( value: temp, child: Text(_formatServingTemp(temp)), ); }).toList(), onChanged: (value) { if (value != null) { setState(() { _selectedServingTemp = value; }); } }, ), ), ], ), const SizedBox(height: 16), DropdownButtonFormField( value: _selectedMilkType, decoration: const InputDecoration( labelText: 'Milk Type (Optional)', border: OutlineInputBorder(), ), items: [ const DropdownMenuItem( value: null, child: Text('No Milk'), ), ...MilkType.values.map((milk) { return DropdownMenuItem( value: milk, child: Text(_formatMilkType(milk)), ); }), ], onChanged: (value) { setState(() { _selectedMilkType = value; }); }, ), ], ); } Widget _buildBrewingParametersSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Brewing Parameters', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextFormField( controller: _coffeeAmountController, decoration: const InputDecoration( labelText: 'Coffee Amount (g) *', border: OutlineInputBorder(), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Coffee amount is required'; } if (double.tryParse(value) == null) { return 'Please enter a valid number'; } return null; }, ), ), const SizedBox(width: 16), Expanded( child: TextFormField( controller: _waterAmountController, decoration: const InputDecoration( labelText: 'Water Amount (ml) *', border: OutlineInputBorder(), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Water amount is required'; } if (double.tryParse(value) == null) { return 'Please enter a valid number'; } return null; }, ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextFormField( controller: _grindSizeController, decoration: const InputDecoration( labelText: 'Grind Size *', hintText: 'e.g., Fine, Medium, Coarse', border: OutlineInputBorder(), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Grind size is required'; } return null; }, ), ), const SizedBox(width: 16), Expanded( child: TextFormField( controller: _brewTimeController, decoration: const InputDecoration( labelText: 'Brew Time (seconds) *', border: OutlineInputBorder(), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Brew time is required'; } if (int.tryParse(value) == null) { return 'Please enter a valid number'; } return null; }, ), ), ], ), ], ); } Widget _buildInstructionsSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Instructions & Notes', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), TextFormField( controller: _instructionsController, decoration: const InputDecoration( labelText: 'Brewing Instructions *', hintText: 'Step-by-step brewing instructions...', border: OutlineInputBorder(), alignLabelWithHint: true, ), maxLines: 4, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Instructions are required'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _notesController, decoration: const InputDecoration( labelText: 'Additional Notes', hintText: 'Tips, variations, or other notes...', border: OutlineInputBorder(), alignLabelWithHint: true, ), maxLines: 3, ), ], ); } String _formatBrewMethod(BrewMethod method) { switch (method) { case BrewMethod.drip: return 'Drip Coffee'; case BrewMethod.frenchPress: return 'French Press'; case BrewMethod.pourOver: return 'Pour Over'; case BrewMethod.espresso: return 'Espresso'; } } String _formatServingTemp(ServingTemp temp) { switch (temp) { case ServingTemp.hot: return 'Hot'; case ServingTemp.cold: return 'Cold'; case ServingTemp.iced: return 'Iced'; } } String _formatMilkType(MilkType milk) { switch (milk) { case MilkType.whole: return 'Whole Milk'; case MilkType.skim: return 'Skim Milk'; case MilkType.soy: return 'Soy Milk'; case MilkType.almond: return 'Almond Milk'; case MilkType.coconut: return 'Coconut Milk'; case MilkType.oat: return 'Oat Milk'; case MilkType.pistachio: return 'Pistachio Milk'; } } void _saveRecipe() async { if (!_formKey.currentState!.validate()) { return; } try { final recipe = Recipe( id: widget.recipe?.id ?? DateTime.now().millisecondsSinceEpoch.toString(), name: _nameController.text.trim(), servingTemp: _selectedServingTemp, milkType: _selectedMilkType, brewMethod: _selectedBrewMethod, grindSize: GrindSize.medium, // Parse from _grindSizeController if needed coffeeAmount: double.parse(_coffeeAmountController.text.trim()), waterAmount: double.parse(_waterAmountController.text.trim()), brewTime: int.parse(_brewTimeController.text.trim()), instructions: _instructionsController.text.trim(), notes: _notesController.text.trim().isEmpty ? null : _notesController.text.trim(), difficulty: Difficulty.intermediate, equipmentNeeded: ['Grinder', 'Scale'], yieldAmount: double.parse(_waterAmountController.text.trim()), caffeinePer100ml: 50.0, waterTemperature: 93, bloomTime: 30, totalExtractionTime: int.parse(_brewTimeController.text.trim()), grindToWaterRatio: '1:16', tags: [], origin: 'User Created', rating: 4.0, popularity: 50, createdBy: 'User', isPublic: false, lastModified: DateTime.now(), ); final appState = Provider.of(context, listen: false); if (widget.recipe == null) { await appState.addRecipe(recipe); } else { await appState.updateRecipe(recipe); } if (mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(widget.recipe == null ? 'Recipe added successfully!' : 'Recipe updated successfully!'), backgroundColor: Theme.of(context).colorScheme.primary, ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error saving recipe: $e'), backgroundColor: Colors.red, ), ); } } } }