124 lines
5.3 KiB
Dart
124 lines
5.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:table_calendar/table_calendar.dart';
|
|
import '../providers/factura_provider.dart';
|
|
import '../providers/servicio_provider.dart';
|
|
import '../providers/configuracion_provider.dart';
|
|
|
|
class CalendarioScreen extends StatefulWidget {
|
|
const CalendarioScreen({super.key});
|
|
|
|
@override
|
|
State<CalendarioScreen> createState() => _CalendarioScreenState();
|
|
}
|
|
|
|
class _CalendarioScreenState extends State<CalendarioScreen> {
|
|
DateTime _focusedDay = DateTime.now();
|
|
DateTime? _selectedDay;
|
|
Map<DateTime, List<Map<String, dynamic>>> _eventos = {};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_cargarEventos();
|
|
}
|
|
|
|
void _cargarEventos() {
|
|
final facturaProv = context.read<FacturaProvider>();
|
|
final servicioProv = context.read<ServicioProvider>();
|
|
facturaProv.cargarFacturas().then((_) {
|
|
_construirEventos(facturaProv, servicioProv);
|
|
});
|
|
}
|
|
|
|
void _construirEventos(FacturaProvider facturaProv, ServicioProvider servicioProv) {
|
|
final eventos = <DateTime, List<Map<String, dynamic>>>{};
|
|
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<FacturaProvider>();
|
|
context.watch<ServicioProvider>();
|
|
final configProv = context.watch<ConfiguracionProvider>();
|
|
final symbol = configProv.config?.moneda == 'USD' ? '\$' : configProv.config?.moneda == 'EUR' ? '€' : 'MX\$';
|
|
|
|
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(
|
|
children: (_eventos[DateTime(_selectedDay!.year, _selectedDay!.month, _selectedDay!.day)] ?? []).map((e) {
|
|
return Card(
|
|
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),
|
|
subtitle: Text('$symbol${NumberFormat('#,##0.00').format(e['monto'])}'),
|
|
trailing: Chip(label: Text(e['estado'] as String, style: TextStyle(fontSize: 12, color: e['estado'] == 'Pagada' ? Colors.green : Colors.orange))),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|