import 'package:sysfarma_mobile/src/app/widgets/app_panel.dart'; import 'package:sysfarma_mobile/src/app/widgets/empty_state_panel.dart'; import 'package:sysfarma_mobile/src/core/network/api_client.dart'; import 'package:sysfarma_mobile/src/core/theme/app_tokens.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class HourReportScreen extends ConsumerStatefulWidget { const HourReportScreen({super.key}); @override ConsumerState createState() => _HourReportScreenState(); } class _HourReportScreenState extends ConsumerState { String _period = 'month'; bool _isLoading = true; Map _data = const {}; @override void initState() { super.initState(); _refresh(); } Future _refresh() async { if (mounted) setState(() => _isLoading = true); final response = await ref.read(apiClientProvider).byHourReport(period: _period); if (!mounted) return; setState(() { _data = response.data ?? const {}; _isLoading = false; }); } void _setPeriod(String p) { if (p == _period) return; setState(() => _period = p); _refresh(); } String _fmtCurrency(Object? value) { final n = double.tryParse('${value ?? 0}') ?? 0.0; return 'S/ ${n.toStringAsFixed(2)}'; } @override Widget build(BuildContext context) { final hours = List>.from(_data['hours'] as List? ?? const []); final active = hours .where((h) => (double.tryParse('${h['total']}') ?? 0) > 0) .toList(); final maxTotal = active.fold( 0, (m, h) => (double.tryParse('${h['total']}') ?? 0) > m ? (double.tryParse('${h['total']}') ?? 0) : m); final peak = _data['peak_hour'] as Map?; return SafeArea( child: RefreshIndicator( onRefresh: _refresh, child: ListView( padding: AppTokens.pagePadding, children: [ Row( children: [ Expanded(child: _PeriodChip(label: 'Hoy', selected: _period == 'date', onTap: () => _setPeriod('date'))), const SizedBox(width: 8), Expanded(child: _PeriodChip(label: 'Mes', selected: _period == 'month', onTap: () => _setPeriod('month'))), ], ), const SizedBox(height: 14), if (_isLoading) const Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center(child: CircularProgressIndicator()), ) else if (active.isEmpty) const EmptyStatePanel( icon: Icons.schedule_rounded, title: 'Sin ventas', subtitle: 'No hay registros en el perĂ­odo.', ) else ...[ if (peak != null) Container( margin: const EdgeInsets.only(bottom: 14), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTokens.primary.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(AppTokens.radiusMedium), border: Border.all(color: AppTokens.primary.withValues(alpha: 0.30)), ), child: Row( children: [ const Icon(Icons.local_fire_department_rounded, color: AppTokens.primary), const SizedBox(width: 10), Expanded( child: Text('Hora pico: ${peak['label']}', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w700)), ), Text(_fmtCurrency(peak['total']), style: Theme.of(context).textTheme.titleSmall?.copyWith( color: AppTokens.primary, fontWeight: FontWeight.w800)), ], ), ), AppPanel( title: 'Ventas por hora', child: Column( children: [ for (final h in active) ...[ _HourRow( hour: h, maxTotal: maxTotal, formatter: _fmtCurrency, ), const SizedBox(height: 10), ], ], ), ), ], ], ), ), ); } } class _HourRow extends StatelessWidget { const _HourRow({required this.hour, required this.maxTotal, required this.formatter}); final Map hour; final double maxTotal; final String Function(Object?) formatter; @override Widget build(BuildContext context) { final total = double.tryParse('${hour['total']}') ?? 0; final ratio = maxTotal > 0 ? (total / maxTotal) : 0.0; return Row( children: [ SizedBox( width: 46, child: Text(hour['label']?.toString() ?? '', style: Theme.of(context).textTheme.labelMedium?.copyWith( color: AppTokens.secondary, fontWeight: FontWeight.w600)), ), Expanded( child: ClipRRect( borderRadius: BorderRadius.circular(6), child: LinearProgressIndicator( value: ratio.clamp(0, 1).toDouble(), minHeight: 18, backgroundColor: AppTokens.border, valueColor: AlwaysStoppedAnimation( AppTokens.primary.withValues(alpha: 0.85)), ), ), ), const SizedBox(width: 10), SizedBox( width: 92, child: Text( formatter(total), textAlign: TextAlign.right, style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w700, ), ), ), ], ); } } class _PeriodChip extends StatelessWidget { const _PeriodChip({required this.label, required this.selected, required this.onTap}); final String label; final bool selected; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppTokens.radiusSmall), onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( color: selected ? AppTokens.primary.withValues(alpha: 0.10) : AppTokens.surface, borderRadius: BorderRadius.circular(AppTokens.radiusSmall), border: Border.all( color: selected ? AppTokens.primary : AppTokens.border, ), ), child: Center( child: Text( label, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: selected ? AppTokens.primary : AppTokens.secondary, fontWeight: selected ? FontWeight.w700 : FontWeight.w500, ), ), ), ), ), ); } }