import 'dart:async'; import 'dart:convert'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:intl/intl.dart'; import 'package:prosapp_web_app/services/api_service.dart'; import 'package:prosapp_web_app/models/schedules_entity.dart'; import 'package:prosapp_web_app/models/service.dart'; import 'package:prosapp_web_app/models/service_location_preferences.dart'; import 'package:prosapp_web_app/models/service_status.dart'; import 'package:prosapp_web_app/models/usuario.dart'; import 'package:prosapp_web_app/models/usuario_profesional.dart'; import 'package:prosapp_web_app/providers/auth_provider.dart'; import 'package:prosapp_web_app/providers/cities_provider.dart'; import 'package:prosapp_web_app/router/router.dart'; import 'package:prosapp_web_app/services/navigation_service.dart'; import 'package:prosapp_web_app/services/notifications_service.dart'; import 'package:prosapp_web_app/ui/cards/white_card.dart'; import 'package:prosapp_web_app/ui/inputs/custom_inputs.dart'; import 'package:prosapp_web_app/ui/widgets/location_picker.dart'; import 'package:flutter/material.dart'; import 'package:prosapp_web_app/utils/local_notifications.dart'; import 'package:prosapp_web_app/utils/network_utility.dart'; import 'package:prosapp_web_app/utils/time_of_day_extension.dart'; import 'package:provider/provider.dart'; class DashboardView extends StatefulWidget { const DashboardView({super.key}); @override State createState() => _DashboardViewState(); } class _DashboardViewState extends State { Usuario? user; List _placesList = []; final TextEditingController _addressController = TextEditingController(); Timer? _debounce; UsuarioProfesional? selectedProfessional; String? selectedProfessionalName; DateTime? selectedDay; TimeOfDay? selectedHour; LatLng? _selectedLatLng; String? _selectedCity; @override void initState() { super.initState(); final authProvider = Provider.of(context, listen: false); setState(() { user = authProvider.user; }); } void placeAutoComplete(String query, String _coords) async { Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", { "input": query, "location": _coords, }); String? response = await NetworkUtility.fetchUrl(uri); if (response != null) { if (mounted) { setState(() { _placesList = jsonDecode(response.toString())['results']; }); } } } Future _selectProfessional(BuildContext context) async { try { List result = await NavigationService.navigateToFuture( Flurorouter.professionalsRoute); final UsuarioProfesional selectedProfessional = result[0]; print('selectedProfessional: $selectedProfessional'); setState(() { selectedDay = result[1]; selectedHour = result[2]; selectedProfessionalName = selectedProfessional.user.name; this.selectedProfessional = selectedProfessional; _addressController.text = selectedProfessional.professionalInfo.address; }); } catch (e) { print('debugeando $e'); } } @override void dispose() { _debounce?.cancel(); _addressController.dispose(); super.dispose(); } Future _openLocationPicker() async { final result = await LocationPickerDialog.show( context, initialPosition: _selectedLatLng, ); if (result != null && mounted) { setState(() { _addressController.text = result.address; _selectedLatLng = result.position; _selectedCity = result.city; }); } } @override Widget build(BuildContext context) { if (user == null) { return const Center( child: CircularProgressIndicator(), ); } bool isUserComplete() { return user!.name != '' && user!.email != '' && user!.phone != '' && user!.city != ''; } final citiesProvider = Provider.of(context); if (citiesProvider.isLoading) { return const Center( child: CircularProgressIndicator(), ); } final _coords = citiesProvider.getCoordsOfCity(user!.city ?? ''); _createService() async { print('debug ${selectedProfessional?.user.id ?? 'user.id'}'); print('debug ${user!.id ?? 'user.id'}'); print('debug ${_addressController.text ?? 'user.id'}'); try { double latitude = 0.0; double longitude = 0.0; print('debug 1'); Service service = Service( id: null, professionalId: selectedProfessional!.user.id, professionalScored: false, userId: user!.id, userScored: false, address: _addressController.text, aditionalAddress: '', latitude: latitude, longitude: longitude, day: selectedDay.toString(), createdAt: DateTime.now().toIso8601String(), description: '', range1Hour1: selectedHour!, range1Hour2: selectedHour!.add(hour: 2), rate: '', status: ServiceStatus.pending, location: ServiceLocationPreferences.delivery, ); print('debug 1'); await ApiService.instance.post('/services', service.toDocument()); print('debug 2'); NotificationsService.showSnackbar('Servicio solicitado exitosamente'); if (selectedProfessional != null) { if (selectedProfessional!.user.token != null) { LocalNotifications.sendPushNotification( selectedProfessional!.user.token!, 'Nuevo servicio', 'Tienes una nueva solicitud de servicio pendiente', ); } } _addressController.clear(); setState(() { selectedProfessional = null; selectedProfessionalName = null; selectedDay = null; selectedHour = null; }); } catch (e) { NotificationsService.showSnackBarError( '$e Error al solicitar el servicio, intenta de nuevo'); } } return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 900), child: Stack( children: [ ListView( physics: const ClampingScrollPhysics(), children: [ WhiteCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), GestureDetector( onTap: isUserComplete() ? _openLocationPicker : () => NotificationsService.showSnackBarError('Completa tu perfil para solicitar un servicio'), child: AbsorbPointer( child: TextFormField( controller: _addressController, decoration: CustomInputs.formInputDecoration( hint: 'Toca para seleccionar tu dirección', label: 'Dirección', icon: Icons.location_on, ), ), ), ), const SizedBox(height: 10), GestureDetector( onTap: () => isUserComplete() ? _selectProfessional(context) : NotificationsService.showSnackBarError( 'Completa tu perfil para solicitar un servicio'), child: AbsorbPointer( child: TextFormField( controller: TextEditingController( text: selectedProfessionalName, ), decoration: CustomInputs.formInputDecoration( hint: 'Selecciona un Profesional', label: 'Profesional', icon: Icons.person_rounded, ), ), ), ), const SizedBox(height: 10), if (selectedDay != null && selectedHour != null) ...[ TextFormField( readOnly: true, controller: TextEditingController( text: selectedDay == null ? '' : DateFormat('dd/MM/yyyy').format(selectedDay!), ), decoration: CustomInputs.formInputDecoration( hint: 'Fecha', label: 'Fecha', icon: Icons.calendar_month, ), ), const SizedBox(height: 10), TextFormField( readOnly: true, controller: TextEditingController( text: selectedHour == null ? '' : ScheduleEntity.getFormatTime(selectedHour), ), decoration: CustomInputs.formInputDecoration( hint: 'Hora', label: 'Hora', icon: Icons.watch_later_outlined, ), ), const SizedBox(height: 10), ], const SizedBox(height: 10), Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 230), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () { if (!isUserComplete()) { NotificationsService.showSnackBarError( 'Completa tu perfil para solicitar un servicio'); return; } _createService(); }, 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( 'Solicitar cita', style: TextStyle(color: Colors.white), ), ), if (selectedDay != null && selectedHour != null && selectedProfessional != null) ...[ const SizedBox(width: 10), ElevatedButton( onPressed: () async { _addressController.clear(); selectedProfessional = null; selectedDay = null; selectedHour = null; setState(() {}); }, style: ButtonStyle( backgroundColor: WidgetStateProperty.all( Colors.red, ), shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(5)))), shadowColor: WidgetStateProperty.all( Colors.transparent)), child: const Icon( Icons.close_rounded, color: Colors.white, ), ), ], ], ), ), ), const SizedBox(height: 8), ], ), ), ], ), Positioned( left: 20, right: 20, top: 80, child: _placesList.isNotEmpty ? Material( elevation: 5.0, borderRadius: BorderRadius.circular(10), child: Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: ListView.builder( shrinkWrap: true, itemCount: _placesList.length, itemBuilder: (context, index) { return ListTile( title: Text(_placesList[index]['formatted_address']), dense: true, visualDensity: VisualDensity.compact, onTap: () { setState(() { _addressController.text = _placesList[index]['formatted_address']; _placesList = []; }); }, ); }, ), ), ) : Container(), ), ], ), ), ); } }