import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; import 'package:prosappco/screens/service/professional_service_screen.dart'; import 'package:prosappco/utils/time_of_day_extension.dart'; import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:injector/injector.dart'; import 'package:professional_repository/professional_repository.dart'; import 'package:intl/intl.dart'; import 'package:prosappco/utils/time_of_day_utils.dart'; import 'package:service_repository/service_repository.dart'; import 'package:setting_repository/setting_repository.dart'; class ProfessionalCalendarScreen extends StatefulWidget { final ProfessionalEntity userProfessional; const ProfessionalCalendarScreen({super.key, required this.userProfessional}); @override State createState() => _ProfessionalCalendarScreenState(); } class _ProfessionalCalendarScreenState extends State { final settingRepository = Injector.appInstance.get(); final serviceRepository = Injector.appInstance.get(); SettingEntity? settings; DateTime today = DateTime.now(); DateTime now = DateTime.now(); late int numDay; List? _services; CalendarFormat _calendarFormat = CalendarFormat.month; bool isLoading = false; @override void initState() { super.initState(); today = DateTime.utc(today.year, today.month, today.day); numDay = today.weekday; _loadSettings(); _loadServices(); } void _loadSettings() { settingRepository.getSettings().then( (value) => setState(() { settings = value; }), ); } void _loadServices() { serviceRepository .getServicesForProfessionalforCalendar(widget.userProfessional.id) .then((services) { setState(() { _services = services; }); }); } void _onDaySelected(DateTime day, DateTime focusedDay) { setState(() { today = day; numDay = today.weekday; }); } void _onFormatChange(CalendarFormat format) { setState(() { _calendarFormat = format; }); } @override Widget build(BuildContext context) { DateTime lastDay = today.add(const Duration(days: 365)); return Scaffold( appBar: AppBar( title: const Text('Calendario'), ), floatingActionButtonLocation: kIsWeb ? FloatingActionButtonLocation.startFloat : FloatingActionButtonLocation.endFloat, resizeToAvoidBottomInset: false, body: BlocProvider( create: (context) => Injector.appInstance.get(), child: BlocConsumer( listener: (context, serviceState) { if (serviceState is CreateServiceLoading) { isLoading = true; } if (serviceState is CreateServiceFailure) { isLoading = false; } }, builder: (context, state) { return 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( _getScheduleFromNumDay(numDay), context), ], ), ), ) ], ); }, ), ), ); } ScheduleEntity? _getScheduleFromNumDay(int numDay) { switch (numDay) { case 1: return widget.userProfessional.schedules.monday; case 2: return widget.userProfessional.schedules.tuesday; case 3: return widget.userProfessional.schedules.wednesday; case 4: return widget.userProfessional.schedules.thursday; case 5: return widget.userProfessional.schedules.friday; case 6: return widget.userProfessional.schedules.saturday; case 7: return widget.userProfessional.schedules.sunday; default: return null; } } List _rangesItems(ScheduleEntity? schedule, BuildContext context) { if (schedule == null || schedule.enabled == false || 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 (ServiceEntity event in events) { if (selectedDay.toString() == 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 (ServiceEntity event in events) { if (selectedDay.toString() == 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 accion 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); context.read().add( CreateService( professionalId: widget.userProfessional.id, userId: widget.userProfessional.id, address: widget.userProfessional.address, aditionalAddress: '', latitude: 0, longitude: 0, day: today.toString(), createdAt: Timestamp.now(), description: '', range1Hour1: time, range1Hour2: time.add(hour: 2), rate: '0', location: ServiceLocationPreferences.office, status: ServiceStatus.selfBooked, ), ); }, child: const Text('Si, 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(); } }