import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/schedule_picker.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/professional_model.dart'; import 'package:prosappco/src/presentation/screens/map/service.dart'; import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:prosappco/src/utils/time_of_day_utils.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:intl/intl.dart'; class CalendarProScreen extends StatefulWidget { final Professional professional; const CalendarProScreen({super.key, required this.professional}); @override State createState() => _CalendarProScreenState(); } class _CalendarProScreenState extends State { final uid = AuthenticationRepository.instance.getCurrentUserUid(); EventoService eventoService = EventoService(); CalendarFormat _calendarFormat = CalendarFormat.month; DateTime today = DateTime.now(); DateTime now = DateTime.now(); late int numDay; List? _events; Map? _horarios; @override void initState() { super.initState(); if (_horarios == null) { Schedule.getHorarios(widget.professional.id.toString()).then( (Map data) { setState(() { _horarios = data; }); }, ); } if (_events == null) { Event.getEventsAllById(widget.professional.id.toString()).then((value) { setState(() { _events = value; }); }); } today = DateTime.utc(today.year, today.month, today.day); numDay = today.weekday; } void _onDaySelected(DateTime day, DateTime focusedDay) { setState(() { today = day; numDay = today.weekday; }); } void _onFormatChange(CalendarFormat format) { setState(() { _calendarFormat = format; }); } @override Widget build(BuildContext context) { if (_events == null) { return const Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } DateTime lastDay = today.add(const Duration(days: 365)); return Scaffold( floatingActionButtonLocation: kIsWeb ? FloatingActionButtonLocation.startFloat : FloatingActionButtonLocation.endFloat, resizeToAvoidBottomInset: false, appBar: PopAppbar( onPressed: () { Navigator.pop(context); }, label: 'Calendario', ), body: Column( children: [ Container( color: const Color.fromARGB(255, 224, 247, 255), child: TableCalendar( locale: 'es_MX', firstDay: DateTime.now(), lastDay: lastDay, focusedDay: today, availableGestures: AvailableGestures.all, onDaySelected: _onDaySelected, selectedDayPredicate: (day) => isSameDay(day, today), calendarFormat: _calendarFormat, onFormatChanged: _onFormatChange, availableCalendarFormats: const { CalendarFormat.month: 'Mes', CalendarFormat.week: 'Semana', CalendarFormat.twoWeeks: '2 Semanas', }, ), ), 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, ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.only(bottom: 15), child: Column( children: [...rangesItems(_horarios?[numDay.toString()])], ), )) ], ), ); } List rangesItems(Schedule? schedule) { if (schedule == null) { return const [ Padding( padding: EdgeInsets.only(top: 20, left: 30, right: 30), child: Text( 'El profesional no acepta turnos este día', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ), ]; } if (!schedule.habilitado) { return const [ Padding( padding: EdgeInsets.only(top: 20, left: 30, right: 30), child: Text( 'El profesional no acepta turnos este día', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ) ]; } if (schedule.jornadaContinua) { List ranges = TimeOfDayUtils.genRanges( schedule.range1Hour1!, schedule.range2Hour2!, ); return rangesItemList(ranges, _events); } else { List ranges1 = TimeOfDayUtils.genRanges( schedule.range1Hour1!, schedule.range1Hour2!, ); List ranges2 = TimeOfDayUtils.genRanges( schedule.range2Hour1!, schedule.range2Hour2!, ); return [ ...rangesItemList(ranges1, _events), ...rangesItemList(ranges2, _events), ]; } } List rangesItemList(List ranges, List? events) { return ranges.map((time) { if (_isHora1Ocupada(time, events)) { return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), child: ListTile( onTap: () { WarningSnackbar.show( title: 'Ocupado', message: 'Este horário ya se encuentra ocupado', ); }, 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( time.format(context), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), subtitle: const Text( 'Ocupado', style: TextStyle( color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold), ), trailing: const Icon( Icons.arrow_forward_ios, color: Colors.grey, ), ), ); } else { return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), child: ListTile( onTap: () { Navigator.pop(context, [today, time, widget.professional]); }, 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( time.format(context), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), subtitle: const Text( 'Disponible', style: TextStyle( color: Colors.green, fontSize: 13, fontWeight: FontWeight.bold), ), trailing: const Icon( Icons.arrow_forward_ios, color: Colors.grey, ), ), ); } }).toList(); } bool _isHora1Ocupada(TimeOfDay hora1, List? events) { if (events != null) { for (Event event in events) { DateTime time1 = DateTime( today.year, today.month, today.day, hora1.hour, hora1.minute, ); if (event.range1Hour1 == time1.toString()) { return true; } } } return false; } }