import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; import '../providers/factura_provider.dart'; import '../providers/servicio_provider.dart'; import '../providers/configuracion_provider.dart'; import '../utils/currency_utils.dart'; class CalendarioScreen extends StatefulWidget { const CalendarioScreen({super.key}); @override State createState() => _CalendarioScreenState(); } class _CalendarioScreenState extends State { DateTime _focusedDay = DateTime.now(); DateTime? _selectedDay; Map>> _eventos = {}; @override void initState() { super.initState(); _cargarEventos(); } void _cargarEventos() { final facturaProv = context.read(); final servicioProv = context.read(); facturaProv.cargarFacturas().then((_) { _construirEventos(facturaProv, servicioProv); }); } void _construirEventos(FacturaProvider facturaProv, ServicioProvider servicioProv) { final eventos = >>{}; for (final f in facturaProv.facturas) { final fecha = DateTime.tryParse(f.fechaVencimiento); if (fecha == null) continue; final servicio = servicioProv.servicios.where((s) => s.id == f.servicioId).firstOrNull; final day = DateTime(fecha.year, fecha.month, fecha.day); eventos.putIfAbsent(day, () => []).add({ 'servicio': servicio?.nombre ?? 'Desconocido', 'monto': f.monto, 'estado': f.estado, 'color': Color(servicio?.color ?? 0xFF6200EE), }); } setState(() => _eventos = eventos); } @override Widget build(BuildContext context) { context.watch(); context.watch(); final configProv = context.watch(); final moneda = configProv.config?.moneda ?? 'USD'; return Scaffold( appBar: AppBar(title: const Text('Calendario de pagos')), body: Column( children: [ TableCalendar( firstDay: DateTime(2024), lastDay: DateTime(2035), focusedDay: _focusedDay, selectedDayPredicate: (day) => isSameDay(_selectedDay, day), eventLoader: (day) { final d = DateTime(day.year, day.month, day.day); return _eventos[d] ?? []; }, calendarStyle: CalendarStyle( todayDecoration: BoxDecoration(color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.5), shape: BoxShape.circle), selectedDecoration: BoxDecoration(color: Theme.of(context).colorScheme.primary, shape: BoxShape.circle), markerDecoration: BoxDecoration(color: Theme.of(context).colorScheme.secondary, shape: BoxShape.circle), ), headerStyle: const HeaderStyle(formatButtonVisible: false, titleCentered: true), onDaySelected: (selected, focused) { setState(() { _selectedDay = selected; _focusedDay = focused; }); }, onPageChanged: (focused) => _focusedDay = focused, calendarBuilders: CalendarBuilders( markerBuilder: (context, date, events) { if (events.isEmpty) return null; final pendientes = events.where((e) => (e as Map)['estado'] == 'Pendiente').length; final pagadas = events.where((e) => (e as Map)['estado'] == 'Pagada').length; return Row( mainAxisSize: MainAxisSize.min, children: [ if (pendientes > 0) Container(margin: const EdgeInsets.only(right: 2), width: 6, height: 6, decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle)), if (pagadas > 0) Container(width: 6, height: 6, decoration: BoxDecoration(color: Colors.green, shape: BoxShape.circle)), ], ); }, ), ), const Divider(), Expanded( child: _selectedDay == null || (_eventos[DateTime(_selectedDay!.year, _selectedDay!.month, _selectedDay!.day)]?.isEmpty ?? true) ? Center(child: Text('Sin facturas en esta fecha', style: Theme.of(context).textTheme.bodyLarge)) : ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: (_eventos[DateTime(_selectedDay!.year, _selectedDay!.month, _selectedDay!.day)] ?? []).length, itemBuilder: (_, i) { final e = _eventos[DateTime(_selectedDay!.year, _selectedDay!.month, _selectedDay!.day)]![i]; final esPagada = e['estado'] == 'Pagada'; return TweenAnimationBuilder( tween: Tween(begin: 0, end: 1), duration: Duration(milliseconds: 250 + i * 80), curve: Curves.easeOutCubic, builder: (context, value, child) { return Opacity( opacity: value, child: Transform.translate( offset: Offset(0, 16 * (1 - value)), child: child, ), ); }, child: Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: CircleAvatar(backgroundColor: e['color'] as Color, child: Text((e['servicio'] as String).substring(0, 2).toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 12))), title: Text(e['servicio'] as String, style: const TextStyle(fontWeight: FontWeight.w500)), subtitle: Text(CurrencyUtils.formatPrice((e['monto'] as num).toDouble(), moneda)), trailing: Chip( label: Text(e['estado'] as String, style: TextStyle(fontSize: 12, color: esPagada ? Colors.green : Colors.orange)), backgroundColor: esPagada ? Colors.green.withValues(alpha: 0.12) : Colors.orange.withValues(alpha: 0.12), side: BorderSide.none, padding: const EdgeInsets.symmetric(horizontal: 4), ), ), ), ); }, ), ), ], ), ); } }