import 'package:prosapp_web_app/models/profesional.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/profile_form_provider.dart'; import 'package:prosapp_web_app/router/router.dart'; import 'package:prosapp_web_app/services/navigation_service.dart'; import 'package:prosapp_web_app/ui/cards/white_card.dart'; import 'package:flutter/material.dart'; import 'package:prosapp_web_app/ui/shared/widgets/schedule_day_tile.dart'; import 'package:provider/provider.dart'; // ── Slot duration options ────────────────────────────────────────────────── const _kSlotOptions = [ (15, '15 min'), (20, '20 min'), (30, '30 min'), (45, '45 min'), (60, '1 hora'), (90, '1 h 30'), (120, '2 horas'), ]; class _SlotDurationPicker extends StatelessWidget { final int value; final ValueChanged onChanged; const _SlotDurationPicker({required this.value, required this.onChanged}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Duración de cada cita', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: _kSlotOptions.map((opt) { final (mins, label) = opt; final selected = value == mins; return ChoiceChip( label: Text(label), selected: selected, onSelected: (_) => onChanged(mins), selectedColor: Colors.blue.shade400, labelStyle: TextStyle( color: selected ? Colors.white : null, fontWeight: selected ? FontWeight.w700 : FontWeight.normal, ), ); }).toList(), ), const SizedBox(height: 4), Text( 'Los usuarios verán bloques de $value min en el calendario.', style: TextStyle(fontSize: 11, color: Colors.grey.shade600), ), ], ), ); } } class ScheduleView extends StatefulWidget { const ScheduleView({super.key}); @override State createState() => _ScheduleViewState(); } class _ScheduleViewState extends State { Usuario? user; late ProfessionalFormProvider professionalFormProvider; @override void initState() { super.initState(); final authProvider = Provider.of(context, listen: false); final profileFormProvider = Provider.of(context, listen: false); professionalFormProvider = Provider.of(context, listen: false); final proProvider = Provider.of(context, listen: false); profileFormProvider.user = authProvider.user; professionalFormProvider.clear(); proProvider.getProfessional(authProvider.user!.id).then((value) { professionalFormProvider.setProfesional(value); }).catchError((_) { professionalFormProvider.setProfesional(Profesional.empty()); }); profileFormProvider.user = authProvider.user; setState(() { user = authProvider.user; }); } bool isTimeRangeValid(TimeOfDay startTime, TimeOfDay endTime) { final start = DateTime(2024, 1, 1, startTime.hour, startTime.minute); final end = DateTime(2024, 1, 1, endTime.hour, endTime.minute); return start.isBefore(end); } @override Widget build(BuildContext context) { final profileFormProvider = Provider.of(context); final user = profileFormProvider.user!; 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: 'Horario', child: Column( children: [ ScheduleDayTile( day: 'Lunes', schedule: profesional.schedules.monday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.monday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(monday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.monday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(monday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( monday: profesional.schedules.monday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( monday: profesional.schedules.monday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( monday: profesional.schedules.monday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( monday: profesional.schedules.monday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Martes', schedule: profesional.schedules.tuesday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.tuesday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(tuesday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.tuesday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(tuesday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( tuesday: profesional.schedules.tuesday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( tuesday: profesional.schedules.tuesday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( tuesday: profesional.schedules.tuesday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( tuesday: profesional.schedules.tuesday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Miercoles', schedule: profesional.schedules.wednesday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.wednesday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(wednesday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.wednesday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(wednesday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( wednesday: profesional.schedules.wednesday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( wednesday: profesional.schedules.wednesday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( wednesday: profesional.schedules.wednesday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( wednesday: profesional.schedules.wednesday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Jueves', schedule: profesional.schedules.thursday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.thursday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(thursday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.thursday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(thursday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( thursday: profesional.schedules.thursday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( thursday: profesional.schedules.thursday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( thursday: profesional.schedules.thursday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( thursday: profesional.schedules.thursday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Viernes', schedule: profesional.schedules.friday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.friday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(friday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.friday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(friday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( friday: profesional.schedules.friday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( friday: profesional.schedules.friday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( friday: profesional.schedules.friday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( friday: profesional.schedules.friday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Sabado', schedule: profesional.schedules.saturday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.saturday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(saturday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.saturday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(saturday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( saturday: profesional.schedules.saturday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( saturday: profesional.schedules.saturday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( saturday: profesional.schedules.saturday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( saturday: profesional.schedules.saturday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider( height: 20, ), ScheduleDayTile( day: 'Domingo', schedule: profesional.schedules.sunday, onEnableChanged: (value) { setState(() { final cur = profesional.schedules.sunday; final updated = (value && cur.range1Hour1 == null) ? cur.copyWith(enabled: true, continuousDay: true, range1Hour1: const TimeOfDay(hour: 8, minute: 0), range2Hour2: const TimeOfDay(hour: 18, minute: 0)) : cur.copyWith(enabled: value); professionalFormProvider.copyProfesionalWith(schedules: profesional.schedules.copyWith(sunday: updated)); }); }, onContinuousDayChanged: (value) { setState(() { final cur = profesional.schedules.sunday; final updated = !value ? cur.copyWith( continuousDay: false, range1Hour2: cur.range1Hour2 ?? const TimeOfDay(hour: 12, minute: 0), range2Hour1: cur.range2Hour1 ?? const TimeOfDay(hour: 14, minute: 0), ) : cur.copyWith(continuousDay: true); professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith(sunday: updated)); }); }, onRange1Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( sunday: profesional.schedules.sunday.copyWith( range1Hour1: pickedTime, ), ), ); }, onRange1Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( sunday: profesional.schedules.sunday.copyWith( range1Hour2: pickedTime, ), ), ); }, onRange2Hour1Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( sunday: profesional.schedules.sunday.copyWith( range2Hour1: pickedTime, ), ), ); }, onRange2Hour2Pick: (pickedTime) { professionalFormProvider.copyProfesionalWith( schedules: profesional.schedules.copyWith( sunday: profesional.schedules.sunday.copyWith( range2Hour2: pickedTime, ), ), ); }, ), const Divider(height: 20), _SlotDurationPicker( value: profesional.slotDurationMinutes, onChanged: (v) => professionalFormProvider .copyProfesionalWith(slotDurationMinutes: v), ), const Divider(height: 20), Container( margin: const EdgeInsets.only(top: 10), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 130), child: ElevatedButton( onPressed: () async { await professionalFormProvider.updateProfesionalProfileScheduleInfo(user.id); NavigationService.replaceTo(Flurorouter.professionalProfileRoute); }, style: ButtonStyle( backgroundColor: WidgetStateProperty.all( Colors.blue.shade400, ), shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(5)), )), shadowColor: WidgetStateProperty.all(Colors.transparent), ), child: const Text('Guardar', style: TextStyle(color: Colors.white)), ), ), ), const SizedBox(height: 10), ], ), ), ), ), ], ); }); } }