import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:trainhub_flutter/core/constants/ui_constants.dart'; import 'package:trainhub_flutter/core/theme/app_colors.dart'; import 'package:trainhub_flutter/domain/entities/training_exercise.dart'; import 'package:trainhub_flutter/domain/entities/training_plan.dart'; import 'package:trainhub_flutter/presentation/plan_editor/models/exercise_drag_data.dart'; import 'package:trainhub_flutter/presentation/plan_editor/plan_editor_controller.dart'; class PlanExerciseTile extends ConsumerStatefulWidget { final TrainingExerciseEntity exercise; final int sectionIndex; final int exerciseIndex; final TrainingPlanEntity plan; const PlanExerciseTile({ super.key, required this.exercise, required this.sectionIndex, required this.exerciseIndex, required this.plan, }); @override ConsumerState createState() => _PlanExerciseTileState(); } class _PlanExerciseTileState extends ConsumerState { bool _isHovered = false; @override Widget build(BuildContext context) { final controller = ref.read( planEditorControllerProvider(widget.plan.id).notifier, ); final dragData = ExerciseDragData( fromSectionIndex: widget.sectionIndex, exerciseIndex: widget.exerciseIndex, exercise: widget.exercise, ); return LongPressDraggable( data: dragData, feedback: Material( elevation: 8, borderRadius: BorderRadius.circular(8), child: Container( width: 220, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: AppColors.zinc800, borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColors.accent.withValues(alpha: 0.5)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.drag_indicator, size: 15, color: AppColors.accent, ), const SizedBox(width: 8), Flexible( child: Text( widget.exercise.name, style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), overflow: TextOverflow.ellipsis, ), ), ], ), ), ), childWhenDragging: Opacity( opacity: 0.35, child: _buildContent(controller), ), child: MouseRegion( onEnter: (_) => setState(() => _isHovered = true), onExit: (_) => setState(() => _isHovered = false), child: _buildContent(controller), ), ); } Widget _buildContent(PlanEditorController controller) { return AnimatedContainer( duration: UIConstants.animationDuration, margin: const EdgeInsets.symmetric(vertical: 3), decoration: BoxDecoration( color: _isHovered ? AppColors.zinc800 : AppColors.zinc900, borderRadius: BorderRadius.circular(8), border: Border.all( color: _isHovered ? AppColors.zinc700 : AppColors.border, ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), child: Column( children: [ // --- Header: drag handle + name + delete --- Row( children: [ const Icon( Icons.drag_handle, size: 15, color: AppColors.zinc600, ), const SizedBox(width: 8), Expanded( child: Text( widget.exercise.name, style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.textPrimary, ), overflow: TextOverflow.ellipsis, ), ), AnimatedOpacity( opacity: _isHovered ? 1.0 : 0.0, duration: UIConstants.animationDuration, child: GestureDetector( onTap: () => controller.removeExerciseFromSection( widget.sectionIndex, widget.exerciseIndex, ), child: const Padding( padding: EdgeInsets.all(2), child: Icon( Icons.close, size: 14, color: AppColors.textMuted, ), ), ), ), ], ), const SizedBox(height: 8), // --- Fields row --- Row( children: [ _FieldBox( label: 'Sets', value: widget.exercise.sets, onChanged: (val) => controller.updateExerciseParams( widget.sectionIndex, widget.exerciseIndex, sets: val, ), ), const SizedBox(width: 8), _FieldBox( label: widget.exercise.isTime ? 'Secs' : 'Reps', value: widget.exercise.value, onChanged: (val) => controller.updateExerciseParams( widget.sectionIndex, widget.exerciseIndex, value: val, ), ), const SizedBox(width: 8), _FieldBox( label: 'Rest(s)', value: widget.exercise.rest, onChanged: (val) => controller.updateExerciseParams( widget.sectionIndex, widget.exerciseIndex, rest: val, ), ), const SizedBox(width: 8), // Time toggle pill GestureDetector( onTap: () => controller.updateExerciseParams( widget.sectionIndex, widget.exerciseIndex, isTime: !widget.exercise.isTime, ), child: AnimatedContainer( duration: UIConstants.animationDuration, padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 6, ), decoration: BoxDecoration( color: widget.exercise.isTime ? AppColors.accentMuted : AppColors.zinc800, borderRadius: BorderRadius.circular(6), border: Border.all( color: widget.exercise.isTime ? AppColors.accent.withValues(alpha: 0.4) : AppColors.border, ), ), child: Icon( Icons.timer_outlined, size: 15, color: widget.exercise.isTime ? AppColors.accent : AppColors.textMuted, ), ), ), ], ), ], ), ), ); } } // --------------------------------------------------------------------------- // Compact field box (label + number input) // --------------------------------------------------------------------------- class _FieldBox extends StatelessWidget { final String label; final int value; final Function(int) onChanged; const _FieldBox({ required this.label, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { return Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: GoogleFonts.inter( fontSize: 10, fontWeight: FontWeight.w500, color: AppColors.textMuted, letterSpacing: 0.3, ), ), const SizedBox(height: 3), SizedBox( height: 32, child: TextField( controller: TextEditingController(text: value.toString()) ..selection = TextSelection.fromPosition( TextPosition(offset: value.toString().length), ), keyboardType: TextInputType.number, textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), decoration: InputDecoration( filled: true, fillColor: AppColors.zinc950, contentPadding: const EdgeInsets.symmetric( horizontal: 4, vertical: 0, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: const BorderSide(color: AppColors.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: const BorderSide(color: AppColors.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: const BorderSide(color: AppColors.zinc500), ), ), onChanged: (val) { final intVal = int.tryParse(val); if (intVal != null) onChanged(intVal); }, ), ), ], ), ); } }