import 'package:auto_route/auto_route.dart'; 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/presentation/plan_editor/plan_editor_controller.dart'; import 'package:trainhub_flutter/presentation/plan_editor/widgets/plan_flow_graph.dart'; import 'package:trainhub_flutter/presentation/plan_editor/widgets/plan_section_card.dart'; @RoutePage() class PlanEditorPage extends ConsumerStatefulWidget { final String planId; const PlanEditorPage({super.key, @PathParam('planId') required this.planId}); @override ConsumerState createState() => _PlanEditorPageState(); } class _PlanEditorPageState extends ConsumerState { bool _graphView = false; @override Widget build(BuildContext context) { final state = ref.watch(planEditorControllerProvider(widget.planId)); final controller = ref.read( planEditorControllerProvider(widget.planId).notifier, ); return Scaffold( backgroundColor: AppColors.surface, body: Column( children: [ // --- Custom header bar --- Container( decoration: const BoxDecoration( color: AppColors.surfaceContainer, border: Border(bottom: BorderSide(color: AppColors.border)), ), padding: const EdgeInsets.symmetric( horizontal: UIConstants.spacing24, vertical: UIConstants.spacing12, ), child: Row( children: [ // Back button Material( color: AppColors.zinc800, borderRadius: BorderRadius.circular(8), child: InkWell( onTap: () => context.router.maybePop(), borderRadius: BorderRadius.circular(8), child: const Padding( padding: EdgeInsets.all(8), child: Icon( Icons.arrow_back, color: AppColors.textSecondary, size: 18, ), ), ), ), const SizedBox(width: UIConstants.spacing16), // Plan icon badge Container( width: 36, height: 36, decoration: BoxDecoration( color: AppColors.accentMuted, borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.description_outlined, color: AppColors.accent, size: 18, ), ), const SizedBox(width: UIConstants.spacing12), // Plan name text field Expanded( child: state.maybeWhen( data: (data) => TextField( controller: TextEditingController(text: data.plan.name) ..selection = TextSelection.fromPosition( TextPosition(offset: data.plan.name.length), ), decoration: const InputDecoration( border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, filled: false, hintText: 'Untitled Plan', contentPadding: EdgeInsets.zero, ), style: GoogleFonts.inter( fontSize: 18, fontWeight: FontWeight.w600, color: AppColors.textPrimary, letterSpacing: -0.3, ), onChanged: controller.updatePlanName, ), orElse: () => Text( 'Loading...', style: GoogleFonts.inter( fontSize: 18, fontWeight: FontWeight.w600, color: AppColors.textMuted, ), ), ), ), // List / graph view toggle _ViewToggle( graphView: _graphView, onChanged: (v) => setState(() => _graphView = v), ), const SizedBox(width: UIConstants.spacing16), // Unsaved changes badge + save button state.maybeWhen( data: (data) => data.isDirty ? Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: UIConstants.spacing12, vertical: 4, ), decoration: BoxDecoration( color: AppColors.warning.withValues( alpha: 0.12, ), borderRadius: BorderRadius.circular(20), border: Border.all( color: AppColors.warning.withValues( alpha: 0.3, ), ), ), child: Text( 'Unsaved changes', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, color: AppColors.warning, ), ), ), const SizedBox(width: UIConstants.spacing12), FilledButton.icon( onPressed: controller.save, icon: const Icon(Icons.save_outlined, size: 16), label: const Text('Save'), ), ], ) : const SizedBox.shrink(), orElse: () => const SizedBox.shrink(), ), ], ), ), // --- Body --- Expanded( child: state.when( data: (data) => _graphView ? PlanFlowGraph( // Rebuild the graph when the plan structure changes. key: ValueKey( '${data.plan.id}-${data.plan.totalExercises}-' '${data.plan.sections.length}', ), plan: data.plan, ) : ReorderableListView.builder( padding: const EdgeInsets.all(UIConstants.spacing24), onReorder: controller.reorderSection, footer: Center( child: Padding( padding: const EdgeInsets.symmetric( vertical: UIConstants.spacing16, ), child: OutlinedButton.icon( onPressed: controller.addSection, icon: const Icon(Icons.add, size: 16), label: const Text('Add Section'), style: OutlinedButton.styleFrom( foregroundColor: AppColors.textSecondary, side: const BorderSide(color: AppColors.border), padding: const EdgeInsets.symmetric( horizontal: UIConstants.spacing24, vertical: UIConstants.spacing12, ), ), ), ), ), itemCount: data.plan.sections.length, itemBuilder: (context, index) { final section = data.plan.sections[index]; return PlanSectionCard( key: ValueKey(section.id), section: section, sectionIndex: index, plan: data.plan, availableExercises: data.availableExercises, ); }, ), error: (e, s) => Center( child: Text( 'Error: $e', style: const TextStyle(color: AppColors.destructive), ), ), loading: () => const Center(child: CircularProgressIndicator()), ), ), ], ), ); } } // --------------------------------------------------------------------------- // List / graph segmented toggle // --------------------------------------------------------------------------- class _ViewToggle extends StatelessWidget { const _ViewToggle({required this.graphView, required this.onChanged}); final bool graphView; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.surfaceContainerHigh, borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColors.border), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _ToggleButton( icon: Icons.view_list_rounded, tooltip: 'List view', selected: !graphView, onTap: () => onChanged(false), ), _ToggleButton( icon: Icons.account_tree_outlined, tooltip: 'Graph view', selected: graphView, onTap: () => onChanged(true), ), ], ), ); } } class _ToggleButton extends StatelessWidget { const _ToggleButton({ required this.icon, required this.tooltip, required this.selected, required this.onTap, }); final IconData icon; final String tooltip; final bool selected; final VoidCallback onTap; @override Widget build(BuildContext context) { return Tooltip( message: tooltip, child: GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( color: selected ? AppColors.accentMuted : Colors.transparent, borderRadius: BorderRadius.circular(7), ), child: Icon( icon, size: 16, color: selected ? AppColors.accent : AppColors.textMuted, ), ), ), ); } }