import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/entities/muscle_group.dart'; import '../controllers/exercise_controller.dart'; /// Modal dialog for logging a resistance-training working set. class LogExerciseDialog extends ConsumerStatefulWidget { const LogExerciseDialog({super.key}); @override ConsumerState createState() => _LogExerciseDialogState(); } class _LogExerciseDialogState extends ConsumerState { final GlobalKey _formKey = GlobalKey(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _setsController = TextEditingController(text: '3'); final TextEditingController _repsController = TextEditingController(text: '8'); final TextEditingController _weightController = TextEditingController(); MuscleGroup _muscleGroup = MuscleGroup.chest; bool _isSaving = false; @override void dispose() { _nameController.dispose(); _setsController.dispose(); _repsController.dispose(); _weightController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Log Exercise'), content: SizedBox( width: 420, child: Form( key: _formKey, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DropdownButtonFormField( initialValue: _muscleGroup, decoration: const InputDecoration( labelText: 'Muscle group', border: OutlineInputBorder(), ), items: [ for (final group in MuscleGroup.values) DropdownMenuItem( value: group, child: Text(group.displayName), ), ], onChanged: (group) => setState( () => _muscleGroup = group ?? _muscleGroup, ), ), const SizedBox(height: 12), TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Exercise', hintText: 'e.g. Back Squat', border: OutlineInputBorder(), ), textCapitalization: TextCapitalization.words, validator: (value) => (value == null || value.trim().isEmpty) ? 'Enter an exercise name' : null, ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextFormField( controller: _setsController, decoration: const InputDecoration( labelText: 'Sets', border: OutlineInputBorder(), isDense: true, ), keyboardType: TextInputType.number, validator: _positiveIntValidator, ), ), const SizedBox(width: 8), Expanded( child: TextFormField( controller: _repsController, decoration: const InputDecoration( labelText: 'Reps', border: OutlineInputBorder(), isDense: true, ), keyboardType: TextInputType.number, validator: _positiveIntValidator, ), ), const SizedBox(width: 8), Expanded( child: TextFormField( controller: _weightController, decoration: const InputDecoration( labelText: 'Weight (lb)', hintText: '0', border: OutlineInputBorder(), isDense: true, ), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), validator: (value) { if (value == null || value.trim().isEmpty) { return null; // bodyweight work } final parsed = double.tryParse(value.trim()); if (parsed == null || parsed < 0) return 'Invalid'; return null; }, ), ), ], ), ], ), ), ), ), actions: [ TextButton( onPressed: _isSaving ? null : () => Navigator.of(context).pop(), child: const Text('Cancel'), ), FilledButton.icon( onPressed: _isSaving ? null : _save, icon: _isSaving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.check), label: const Text('Log Set'), ), ], ); } String? _positiveIntValidator(String? value) { final parsed = int.tryParse((value ?? '').trim()); if (parsed == null || parsed <= 0) return 'Must be > 0'; return null; } Future _save() async { if (!(_formKey.currentState?.validate() ?? false)) return; setState(() => _isSaving = true); final saved = await ref.read(exerciseControllerProvider.notifier).logExercise( muscleGroup: _muscleGroup, exerciseName: _nameController.text, sets: int.parse(_setsController.text.trim()), reps: int.parse(_repsController.text.trim()), weightLbs: _weightController.text.trim().isEmpty ? 0 : double.parse(_weightController.text.trim()), ); if (!mounted) return; if (saved) { Navigator.of(context).pop(); } else { setState(() => _isSaving = false); } } }