import 'package:intl/intl.dart'; import 'package:prosapp_web_app/models/profesional.dart'; import 'package:prosapp_web_app/models/schedules_entity.dart'; import 'package:prosapp_web_app/models/service.dart'; import 'package:prosapp_web_app/models/usuario.dart'; import 'package:prosapp_web_app/providers/auth_provider.dart'; import 'package:prosapp_web_app/providers/professional_form_provider.dart'; import 'package:prosapp_web_app/providers/professional_provider.dart'; import 'package:prosapp_web_app/providers/services_provider.dart'; import 'package:prosapp_web_app/ui/cards/white_card.dart'; import 'package:prosapp_web_app/utils/time_of_day_utils.dart'; import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:flutter/material.dart'; class ProfessionalCalendarView extends StatefulWidget { const ProfessionalCalendarView({super.key}); @override State createState() => _ProfessionalCalendarViewState(); } class _ProfessionalCalendarViewState extends State { DateTime today = DateTime.now(); late int numDay; List? _services; Usuario? user; @override void initState() { super.initState(); numDay = today.weekday; _fetchProfessionalAndServices(); } void _fetchProfessionalAndServices() async { final authProvider = Provider.of(context, listen: false); final professionalFormProvider = Provider.of(context, listen: false); final servicesProvider = Provider.of(context, listen: false); final proProvider = Provider.of(context, listen: false); final professional = await proProvider.getProfessional(authProvider.user!.id); professionalFormProvider.setProfesional(professional); final services = await servicesProvider.getServicesForProfessional(professional.id); setState(() { _services = services; user = authProvider.user; }); } void _onDaySelected(DateTime day, DateTime focusedDay) { setState(() { today = day; numDay = today.weekday; }); _fetchProfessionalAndServices(); } @override Widget build(BuildContext context) { return Consumer( builder: (context, professionalFormProvider, child) { if (professionalFormProvider.profesional == null) { return const Center( child: CircularProgressIndicator(), ); } final profesional = professionalFormProvider.profesional!; return ListView( physics: const ClampingScrollPhysics(), children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 900), child: WhiteCard( title: 'Calendario', child: Column( children: [ TableCalendar( locale: 'es_CO', firstDay: DateTime.now(), lastDay: DateTime.utc(2030, 3, 14), focusedDay: today, availableGestures: AvailableGestures.all, onDaySelected: _onDaySelected, selectedDayPredicate: (day) => isSameDay(day, today), ), const Divider(height: 0), SizedBox( width: double.infinity, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 8, ), child: Text( DateFormat('dd MMMM yyyy', 'es').format(today), style: const TextStyle( color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), const Divider(height: 0), Column( children: [ ..._rangesItems(_getScheduleFromNumDay(numDay, profesional),context), ], ), ], ), ), ), ), ], ); }, ); } ScheduleEntity? _getScheduleFromNumDay(int numDay, Profesional userProfessional) { switch (numDay) { case 1: return userProfessional.schedules.monday; case 2: return userProfessional.schedules.tuesday; case 3: return userProfessional.schedules.wednesday; case 4: return userProfessional.schedules.thursday; case 5: return userProfessional.schedules.friday; case 6: return userProfessional.schedules.saturday; case 7: return userProfessional.schedules.sunday; default: return null; } } List _rangesItems(ScheduleEntity? schedule, BuildContext context) { if (schedule == null || !schedule.enabled || schedule.range1Hour1 == null || schedule.range2Hour2 == null) { return [ const Padding( padding: EdgeInsets.symmetric(vertical: 25), child: Text("No hay horarios disponibles"), ) ]; } if (schedule.continuousDay) { List ranges = TimeOfDayUtils.genRanges( schedule.range1Hour1!, schedule.range2Hour2!, ); return rangesItemList(ranges, _services, today, context); } List ranges1 = TimeOfDayUtils.genRanges( schedule.range1Hour1!, schedule.range1Hour2!, ); List ranges2 = TimeOfDayUtils.genRanges( schedule.range2Hour1!, schedule.range2Hour2!, ); return [ ...rangesItemList(ranges1, _services, today, context), ...rangesItemList(ranges2, _services, today, context), ]; } bool _isHora1Ocupada( TimeOfDay hora1, List? events, DateTime selectedDay) { if (events != null) { for (Service event in events) { if (selectedDay.toIso8601String().split('T').first == event.day) { if (hora1 == event.range1Hour1) { return true; } } } } return false; } List rangesItemList(List ranges, List? events, DateTime selectedDay, BuildContext context) { return ranges.map((time) { if (_isHora1Ocupada(time, events, selectedDay)) { return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), child: ListTile( onTap: () { if (events != null) { for (Service event in events) { if (selectedDay.toIso8601String().split('T').first == event.day) { if (time == event.range1Hour1) { if (event.userId == event.professionalId) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Horario ocupado por ti'), ), ); } else { // Navigator.push( // context, // CupertinoPageRoute( // builder: (context) => ProfessionalServiceScreen( // serviceId: event.id!, // ), // ), // ); } } } } } }, contentPadding: const EdgeInsets.all(16), leading: Container( width: 40, height: 40, decoration: const BoxDecoration( gradient: LinearGradient( colors: [Colors.yellow, Colors.red, Colors.red], begin: Alignment.topLeft, end: Alignment.bottomRight, ), shape: BoxShape.circle, ), child: const Center( child: Icon( Icons.access_time, color: Colors.white, ), ), ), title: Text( ScheduleEntity.getFormatTime(time) ?? '', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), subtitle: const Text( 'Ocupado', style: TextStyle( color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold), ), ), ); } else { return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), child: ListTile( onTap: () { showDialog( context: context, builder: (BuildContext dialogContext) { return AlertDialog( title: const Text('Reservar hora'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( '¿Estás seguro de que deseas reservar a las ${ScheduleEntity.getFormatTime(time)} del ${DateFormat('dd-MM-yyyy').format(today)}?', ), const SizedBox(height: 10), const Text( '⚠️ Esta acción no se puede deshacer ⚠️', style: TextStyle(fontWeight: FontWeight.bold), ), ], ), actions: [ TextButton( onPressed: () { Navigator.pop(dialogContext); }, child: const Text('No, cancelar'), ), TextButton( onPressed: () { Navigator.pop(dialogContext); Navigator.pop(context); }, child: const Text('Sí, reservar'), ), ], ); }, ); }, contentPadding: const EdgeInsets.all(16), leading: Container( width: 40, height: 40, decoration: const BoxDecoration( gradient: LinearGradient( colors: [Colors.blue, Colors.green], begin: Alignment.topLeft, end: Alignment.bottomRight, ), shape: BoxShape.circle, ), child: const Center( child: Icon( Icons.access_time, color: Colors.white, ), ), ), title: Text( ScheduleEntity.getFormatTime(time) ?? '', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), subtitle: const Text( 'Disponible', style: TextStyle( color: Colors.green, fontSize: 13, fontWeight: FontWeight.bold, ), ), ), ); } }).toList(); } }