import 'package:flutter/material.dart'; import '../theme/app_colors.dart'; /// Visual time block widget showing estimated vs actual duration. /// /// - Horizontal bar: green (under estimate) -> yellow (near) -> soft orange (over). /// - NO red — going over is shown gently, not punitively. class TimeVisualizer extends StatelessWidget { /// Estimated duration in minutes. final int estimatedMinutes; /// Actual duration in minutes (null if not yet completed). final int? actualMinutes; /// Optional height for the bars. final double barHeight; const TimeVisualizer({ super.key, required this.estimatedMinutes, this.actualMinutes, this.barHeight = 12, }); /// Returns a color based on actual / estimated ratio. /// <= 0.8 -> green (under) /// 0.8-1.1 -> teal (on time) /// 1.1-1.5 -> yellow (slightly over) /// > 1.5 -> soft orange (over, but gentle) Color _barColor(double ratio) { if (ratio <= 0.8) return AppColors.completed; if (ratio <= 1.1) return AppColors.primary; if (ratio <= 1.5) return AppColors.energyMedium; return AppColors.energyHigh; // soft orange, NOT red } @override Widget build(BuildContext context) { final theme = Theme.of(context); final actual = actualMinutes; final hasActual = actual != null && actual > 0; // If no actual yet, show just the estimate bar. final ratio = hasActual ? actual / estimatedMinutes : 0.0; final estimateWidth = 1.0; // always full width = estimate final actualWidth = hasActual ? ratio.clamp(0.0, 2.0) / 2.0 : 0.0; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Labels Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Estimated: $estimatedMinutes min', style: theme.textTheme.bodySmall, ), if (hasActual) Text( 'Actual: $actual min', style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w600, color: _barColor(ratio), ), ), ], ), const SizedBox(height: 6), // ── Estimate bar (background / reference) ────────────────── ClipRRect( borderRadius: BorderRadius.circular(barHeight / 2), child: Stack( children: [ // Full estimate background Container( height: barHeight, width: double.infinity, decoration: BoxDecoration( color: AppColors.primary.withAlpha(30), borderRadius: BorderRadius.circular(barHeight / 2), ), ), // Actual fill if (hasActual) FractionallySizedBox( widthFactor: actualWidth.clamp(0.0, estimateWidth), child: Container( height: barHeight, decoration: BoxDecoration( color: _barColor(ratio), borderRadius: BorderRadius.circular(barHeight / 2), ), ), ), ], ), ), // ── Gentle insight ───────────────────────────────────────── if (hasActual) ...[ const SizedBox(height: 4), Text( _insightText(ratio), style: theme.textTheme.bodySmall?.copyWith( color: _barColor(ratio), fontWeight: FontWeight.w500, ), ), ], ], ); } String _insightText(double ratio) { if (ratio <= 0.8) return 'Finished early — nice!'; if (ratio <= 1.1) return 'Right on target.'; if (ratio <= 1.5) return 'A little over — totally fine.'; return 'Took longer than expected. That happens!'; } }