import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:trainhub_flutter/core/theme/app_colors.dart'; import 'package:trainhub_flutter/domain/entities/training_plan.dart'; import 'package:trainhub_flutter/presentation/common/widgets/node_graph_canvas.dart'; /// Graph view of a training plan: one column per section, exercises chained /// top-to-bottom, sections chained left-to-right — the session flow at a /// glance. Nodes can be dragged to rearrange the picture; the plan structure /// itself is edited in the list view. class PlanFlowGraph extends StatefulWidget { const PlanFlowGraph({super.key, required this.plan}); final TrainingPlanEntity plan; @override State createState() => _PlanFlowGraphState(); } class _PlanFlowGraphState extends State { /// User-dragged overrides on top of the auto layout, per node id. final Map _overrides = {}; static const double _colWidth = 250; static const double _rowHeight = 84; @override Widget build(BuildContext context) { final nodes = []; final edges = []; Offset pos(String id, Offset fallback) => _overrides[id] ?? fallback; nodes.add( GraphNodeData( id: 'start', title: 'Start Session', subtitle: widget.plan.name, accent: AppColors.success, position: pos('start', const Offset(40, 40)), ), ); var previousTail = 'start'; for (var s = 0; s < widget.plan.sections.length; s++) { final section = widget.plan.sections[s]; final x = 40.0 + (s + 1) * _colWidth; final sectionId = 'sec-${section.id}'; nodes.add( GraphNodeData( id: sectionId, title: section.name, subtitle: '${section.exercises.length} drills', accent: AppColors.accent, position: pos(sectionId, Offset(x, 40)), ), ); edges.add(GraphEdgeData(previousTail, sectionId)); var tail = sectionId; for (var e = 0; e < section.exercises.length; e++) { final exercise = section.exercises[e]; final id = 'ex-${exercise.instanceId}'; final detail = exercise.isTime ? '${exercise.sets} × ${exercise.value}s · rest ${exercise.rest}s' : '${exercise.sets} × ${exercise.value} · rest ${exercise.rest}s'; nodes.add( GraphNodeData( id: id, title: exercise.name, subtitle: detail, accent: AppColors.info, position: pos(id, Offset(x + 24, 40 + (e + 1) * _rowHeight)), ), ); edges.add(GraphEdgeData(tail, id)); tail = id; } previousTail = tail; } return Stack( children: [ ClipRect( child: NodeGraphCanvas( nodes: nodes, edges: edges, canvasSize: Size( (widget.plan.sections.length + 2) * _colWidth + 600, 2000, ), onNodeMoved: (id, position) => setState(() => _overrides[id] = position), ), ), Positioned( left: 16, bottom: 16, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: AppColors.surfaceContainer.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColors.border), ), child: Text( 'SESSION FLOW · DRAG TO REARRANGE · EDIT IN LIST VIEW', style: GoogleFonts.jetBrainsMono( fontSize: 9, letterSpacing: 0.8, color: AppColors.textMuted, ), ), ), ), ], ); } }