import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:intl/intl.dart'; import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/models/setting_model.dart'; import 'package:prosappco/src/presentation/screens/chat.dart'; import 'package:prosappco/src/presentation/screens/score.dart'; import 'package:prosappco/src/services/api_service.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:community_material_icon/community_material_icon.dart'; class CitaScreen extends StatefulWidget { final Event evento; const CitaScreen({super.key, required this.evento}); @override State createState() => _CitaScreenState(); } class _CitaScreenState extends State { final uid = AuthenticationRepository.instance.getCurrentUserUid(); DateTime today = DateTime.now(); String nombre = ''; String numberPhone = ''; String? photoUrl; ScoresModel? scoresModel; bool? ver = true; bool? pro; String formatCurrency(int number) { final formatter = NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: ''); return '\$${formatter.format(number)}'; } Future _openMap(double lat, double lng) async { final Uri url = Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng'); if (!await launchUrl(url)) { throw Exception('Could not launch $url'); } } Future _sendWhatsapp(String phoneNumber) async { final whatsappUrl = 'https://wa.me/$phoneNumber?text=${Uri.parse('Hola! me contactaste por Prossapp')}'; if (!await launch(whatsappUrl)) { throw Exception('Could not launch $whatsappUrl'); } } SettingModel? settings; @override void initState() { super.initState(); if (settings == null) { SettingModel.getSettings().then( (SettingModel value) => setState(() => settings = value), ); } if (scoresModel == null) { if (uid != widget.evento.userId) { ScoresModel.scoreTo(widget.evento.userId, false, false).then( (ScoresModel s) => setState(() { scoresModel = s; pro = true; }), ); } else { ScoresModel.scoreTo(widget.evento.professionalId, true, false).then( (ScoresModel s) => setState(() { scoresModel = s; pro = false; }), ); } } _loadOtherUser(); } Future _loadOtherUser() async { final targetId = (uid != widget.evento.userId) ? widget.evento.userId : widget.evento.professionalId; try { final Map data = await ApiService.instance.get('/users/$targetId'); if (mounted) { setState(() { nombre = data['name'] ?? ''; photoUrl = data['picture']; numberPhone = data['phone'] ?? ''; }); } } catch (e) { print('Error loading user: $e'); } } static const _toBackendStatus = { 'pendiente': 'pending', 'aprobado': 'accepted', 'denegado': 'cancelled', 'iniciado': 'active', 'terminado': 'completed', }; Future _updateStatus(String status) async { try { final backendStatus = _toBackendStatus[status] ?? status; await ApiService.instance .patch('/services/${widget.evento.id}/status', {'status': backendStatus}); } catch (e) { print('Error updating service status: $e'); } } @override Widget build(BuildContext context) { final eventDate = DateFormat('yyyy-MM-dd').parse(widget.evento.day); return Scaffold( appBar: PopAppbar( onPressed: () { Navigator.pop(context); }, label: 'Servicio'), body: Column( children: [ Expanded( child: Column( children: [ ListTile( leading: ReferencePhoto( ref: photoUrl, size: 50, sizeCircle: 50, sizeIcon: 35, ), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( nombre, style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16, ), ), Text( '${DateFormat('dd MMMM', 'es').format(DateTime.parse(widget.evento.day))} ${DateFormat('h:mm a').format(DateTime.parse(widget.evento.range1Hour1))}', style: const TextStyle( color: Colors.grey, fontSize: 16, ), ) ], ), subtitle: Row( children: [ RatingBar.builder( initialRating: scoresModel?.average ?? 0, minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 25, maxRating: 5, itemPadding: const EdgeInsets.symmetric(horizontal: 0), itemBuilder: (context, _) => const Icon( Icons.star, color: Color(0xFF2BA4EC), ), onRatingUpdate: (rating) {}, ignoreGestures: true, ), const SizedBox(width: 5), Text( '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'), ], ), ), widget.evento.userId == widget.evento.professionalId ? const SizedBox() : Container( margin: const EdgeInsets.only( left: 40, right: 40, top: 20, bottom: 20), padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 15), decoration: BoxDecoration( color: const Color(0xFFD6F4FF), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 1, blurRadius: 5, offset: const Offset(1, 3), ), ], ), child: Row( children: [ const Icon( Icons.error_outline, size: 27, color: Colors.black54, ), const SizedBox(width: 15), widget.evento.ubicacion != 'sitio' ? const Text( 'Servicio a domicilio.', style: TextStyle( color: Colors.black, fontSize: 14), ) : const Text( 'Servicio en su sitio / consultorio', style: TextStyle( color: Colors.black, fontSize: 14), ), ], ), ), settings?.tarifas == true && widget.evento.tarifa != 0 ? Column( children: [ Text( formatCurrency(widget.evento.tarifa ?? 0), style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 25), ), const Text('Tarifa consulta', style: TextStyle(fontSize: 15)), ], ) : const SizedBox(), const SizedBox(height: 15), Text( textAlign: TextAlign.center, '"${widget.evento.description?.trim()}"', style: const TextStyle( color: Colors.grey, fontStyle: FontStyle.italic), ), widget.evento.userId == widget.evento.professionalId ? const SizedBox() : widget.evento.status == 'accepted' || widget.evento.status == 'active' ? const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Text( 'Medios de comunicación con el usuario.', style: TextStyle(color: Color(0xFF2BA4EC)), ), ) : const SizedBox(height: 10), widget.evento.userId == widget.evento.professionalId ? const SizedBox() : widget.evento.status == 'completed' ? pro == true ? widget.evento.professionalScored == true ? const SizedBox() : Column( children: [ const SizedBox(height: 120), ElevatedButton( onPressed: () { Navigator.pushReplacement( context, CupertinoPageRoute( builder: (BuildContext context) { return ScoreScreen( evento: widget.evento, pro: pro!, ); }, ), ); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Puntuar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ), ], ) : widget.evento.userScored == true ? const SizedBox() : Column( children: [ const SizedBox(height: 120), ElevatedButton( onPressed: () { Navigator.pushReplacement( context, CupertinoPageRoute( builder: (BuildContext context) { return ScoreScreen( evento: widget.evento, pro: pro!, ); }, ), ); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Puntuar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ), ], ) : widget.evento.status == 'accepted' || widget.evento.status == 'active' ? Row( children: [ const Expanded(child: SizedBox()), ElevatedButton( onPressed: () => launch("tel:$numberPhone"), style: ElevatedButton.styleFrom( foregroundColor: const Color(0xFF2BA4EC), backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), side: const BorderSide( color: Color(0xFF2BA4EC), width: 2, ), ), ), child: const Padding( padding: EdgeInsets.symmetric( vertical: 18, horizontal: 0), child: Icon( Icons.phone_android, size: 30, color: Color(0xFF2BA4EC), ), ), ), const SizedBox(width: 20), ElevatedButton( onPressed: () async { if (widget.evento.status != 'pending') { // Start or retrieve chat via API try { await ApiService.instance.post( '/chat/start/${widget.evento.professionalId}', {}); } catch (_) {} Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { return ChatScreen( eventoId: widget.evento.id); }, ), ); } else { showAppSnackBar( 'El profesional aún no ha aceptado', 'Debes esperar a que el profesional acepte tu solicitud.', color: Colors.grey.shade700, ); } }, style: ElevatedButton.styleFrom( foregroundColor: const Color(0xFF2BA4EC), backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), side: const BorderSide( color: Color(0xFF2BA4EC), width: 2, ), ), ), child: const Padding( padding: EdgeInsets.symmetric( vertical: 18, horizontal: 0), child: Icon( Icons.message, size: 30, color: Color(0xFF2BA4EC), ), ), ), const SizedBox(width: 20), ElevatedButton( onPressed: () { _sendWhatsapp(numberPhone); }, style: ElevatedButton.styleFrom( foregroundColor: const Color(0xFF2BA4EC), backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), side: const BorderSide( color: Color(0xFF2BA4EC), width: 2, ), ), ), child: const Padding( padding: EdgeInsets.symmetric( vertical: 18, horizontal: 0), child: Icon( CommunityMaterialIcons.whatsapp, size: 30, color: Color(0xFF2BA4EC), ), ), ), const Expanded(child: SizedBox()), ], ) : const SizedBox(), widget.evento.ubicacion == 'sitio' ? const SizedBox() : Padding( padding: const EdgeInsets.only(top: 40), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () { _openMap(widget.evento.latitud!, widget.evento.longitud!); }, style: ElevatedButton.styleFrom( foregroundColor: const Color(0xFF2BA4EC), backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), side: const BorderSide( color: Color(0xFF2BA4EC), width: 2, ), ), ), child: const Padding( padding: EdgeInsets.symmetric( vertical: 18, horizontal: 0), child: Icon( Icons.near_me, size: 30, color: Color(0xFFFFFFFF), ), ), ), const SizedBox(width: 20), SizedBox( width: 200, child: Text('${widget.evento.address}'), ) ], ), ), ], ), ), widget.evento.status == 'accepted' ? Padding( padding: const EdgeInsets.only(bottom: 30), child: Column( children: [ Padding( padding: const EdgeInsets.only(bottom: 20), child: eventDate.year == today.year && eventDate.month == today.month && eventDate.day == today.day ? (DateTime.now() .difference(DateTime.parse( widget.evento.range1Hour1)) .abs() <= const Duration(minutes: 30) && ver == true) ? ElevatedButton( onPressed: () async { await _updateStatus('iniciado'); setState(() => ver = false); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Iniciar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ) : const SizedBox() : const SizedBox(), ), widget.evento.professionalId == uid ? ElevatedButton( onPressed: () async { await _updateStatus('denegado'); Navigator.pushReplacementNamed( context, '/solicitud'); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFEC2B2B), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Cancelar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ) : const SizedBox(), ], ), ) : const SizedBox(), widget.evento.status == 'pending' ? Padding( padding: const EdgeInsets.only(bottom: 30), child: Column( children: [ Padding( padding: const EdgeInsets.only(bottom: 20), child: widget.evento.professionalId == uid ? ElevatedButton( onPressed: () async { await _updateStatus('aprobado'); Navigator.pushReplacementNamed( context, '/solicitud'); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Aceptar', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ) : const SizedBox(), ), ElevatedButton( onPressed: () async { await _updateStatus('denegado'); Navigator.pushReplacementNamed(context, '/solicitud'); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFEC2B2B), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Cancelar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ), ], ), ) : const SizedBox(), widget.evento.status == 'active' || ver == false ? Padding( padding: const EdgeInsets.only(bottom: 30), child: Column( children: [ ElevatedButton( onPressed: () async { await _updateStatus('terminado'); Navigator.pushReplacement( context, CupertinoPageRoute( builder: (BuildContext context) { return ScoreScreen( evento: widget.evento, pro: pro!, ); }, ), ); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2BA4EC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), elevation: 0, minimumSize: const Size(230, 60), ), child: const Text( 'Terminar servicio', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ), ], ), ) : const SizedBox(), ], ), ); } }