import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../providers/reports_provider.dart'; import 'report_card_wrapper.dart'; /// Formats hours into a human-readable string. String _formatHours(double h) { if (h < 1) { return '${(h * 60).round()}m'; } if (h >= 24) { final days = (h / 24).floor(); final rem = (h % 24).round(); return rem > 0 ? '${days}d ${rem}h' : '${days}d'; } return '${h.toStringAsFixed(1)}h'; } /// Horizontal bar chart — average task resolution time (hours) per office. /// Limited to the top 10 slowest offices. Uses custom Flutter widgets so /// labels sit genuinely inside each bar. class AvgResolutionChart extends ConsumerWidget { const AvgResolutionChart({super.key, this.repaintKey}); final GlobalKey? repaintKey; @override Widget build(BuildContext context, WidgetRef ref) { final asyncData = ref.watch(avgResolutionReportProvider); return asyncData.when( loading: () => ReportCardWrapper( title: 'Avg Resolution Time by Office', isLoading: true, height: 320, repaintBoundaryKey: repaintKey, child: const SizedBox.shrink(), ), error: (e, _) => ReportCardWrapper( title: 'Avg Resolution Time by Office', error: e.toString(), repaintBoundaryKey: repaintKey, child: const SizedBox.shrink(), ), data: (data) => _build(context, data), ); } Widget _build(BuildContext context, List rawData) { final data = rawData.length > 10 ? rawData.sublist(0, 10) : rawData; if (data.isEmpty) { return ReportCardWrapper( title: 'Avg Resolution Time by Office', repaintBoundaryKey: repaintKey, child: const SizedBox( height: 200, child: Center(child: Text('No data for selected period')), ), ); } final maxHours = data.fold( 0, (m, e) => e.avgHours > m ? e.avgHours : m, ); final height = (data.length * 34.0).clamp(160.0, 420.0); final colors = Theme.of(context).colorScheme; final barColor = colors.error.withValues(alpha: 0.75); final onBarColor = barColor.computeLuminance() > 0.5 ? Colors.black87 : Colors.white; return ReportCardWrapper( title: 'Avg Resolution Time by Office', repaintBoundaryKey: repaintKey, height: height, child: LayoutBuilder( builder: (context, constraints) { final availableWidth = constraints.maxWidth; final labelStyle = Theme.of(context).textTheme.labelSmall?.copyWith( color: onBarColor, fontWeight: FontWeight.w600, ); return Column( mainAxisAlignment: MainAxisAlignment.center, children: data.map((item) { final fraction = maxHours > 0 ? item.avgHours / maxHours : 0.0; final barWidth = (fraction * availableWidth).clamp( 120.0, availableWidth, ); final timeLabel = _formatHours(item.avgHours); return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Tooltip( message: '${item.officeName}: $timeLabel', child: Align( alignment: Alignment.centerLeft, child: Container( width: barWidth, height: 28, decoration: BoxDecoration( color: barColor, borderRadius: const BorderRadius.horizontal( right: Radius.circular(6), ), ), padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( children: [ Expanded( child: Text( item.officeName, style: labelStyle, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 6), Text(timeLabel, style: labelStyle), ], ), ), ), ), ); }).toList(), ); }, ), ); } }