diff --git a/android/app/build.gradle b/android/app/build.gradle index 8b67127..d5609a5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -46,7 +46,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.prosappco" + applicationId "com.prosapp.prosapp" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion "21".toInteger() diff --git a/android/app/google-services.json b/android/app/google-services.json index bebbe34..c74eef4 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -8,18 +8,18 @@ "client": [ { "client_info": { - "mobilesdk_app_id": "1:245204384533:android:12920d0fe3ec9b999f871c", + "mobilesdk_app_id": "1:245204384533:android:e01772831d86f5c79f871c", "android_client_info": { - "package_name": "com.example.prosappco" + "package_name": "com.prosapp.prosapp" } }, "oauth_client": [ { - "client_id": "245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com", + "client_id": "245204384533-23bm14294ctm728nd9u83eiapsjfebhu.apps.googleusercontent.com", "client_type": 1, "android_info": { - "package_name": "com.example.prosappco", - "certificate_hash": "e0e88a66931385d2626f8531e7cbbb5722df7b4b" + "package_name": "com.prosapp.prosapp", + "certificate_hash": "29ac5d7d1b88c755a084a0ad5d561dd62d795eb5" } }, { diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index bcbbad0..ee0c04f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + ADF3D9B31D73179ECCE7D3BB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -33,6 +34,7 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -88,6 +90,7 @@ 97C146EF1CF9000F007C117D /* Products */, E5C876AEF0D01DE572D83FC4 /* Pods */, 5F3EBDC617D1ACB18B4906E0 /* Frameworks */, + 47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */, ); sourceTree = ""; }; @@ -192,6 +195,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ADF3D9B31D73179ECCE7D3BB /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f3cd0f2..00313e9 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -5,15 +5,15 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Prosappco + Prosapp CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + com.prosapp.prosapp CFBundleInfoDictionaryVersion 6.0 CFBundleName - prosappco + prosapp CFBundlePackageType APPL CFBundleShortVersionString diff --git a/ios/firebase_app_id_file.json b/ios/firebase_app_id_file.json index a4bc04d..1c89e48 100644 --- a/ios/firebase_app_id_file.json +++ b/ios/firebase_app_id_file.json @@ -1,7 +1,7 @@ { "file_generated_by": "FlutterFire CLI", "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", - "GOOGLE_APP_ID": "1:245204384533:ios:aeb233e55c5ddf7a9f871c", + "GOOGLE_APP_ID": "1:245204384533:ios:1d58f5624d0df4859f871c", "FIREBASE_PROJECT_ID": "prosapp-5747a", "GCM_SENDER_ID": "245204384533" } \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 97619e5..c5f1446 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -49,35 +49,40 @@ class DefaultFirebaseOptions { messagingSenderId: '245204384533', projectId: 'prosapp-5747a', authDomain: 'prosapp-5747a.firebaseapp.com', + databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com', storageBucket: 'prosapp-5747a.appspot.com', + measurementId: 'G-EVBEQRZEDY', ); static const FirebaseOptions android = FirebaseOptions( apiKey: 'AIzaSyBp7vsJvD6oXz0FORBLEeIELwId4a2onHQ', - appId: '1:245204384533:android:12920d0fe3ec9b999f871c', + appId: '1:245204384533:android:e01772831d86f5c79f871c', messagingSenderId: '245204384533', projectId: 'prosapp-5747a', + databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com', storageBucket: 'prosapp-5747a.appspot.com', ); static const FirebaseOptions ios = FirebaseOptions( apiKey: 'AIzaSyDUhnkNkeEPkqcwVo5EWr63q3oEX6bDGic', - appId: '1:245204384533:ios:aeb233e55c5ddf7a9f871c', + appId: '1:245204384533:ios:1d58f5624d0df4859f871c', messagingSenderId: '245204384533', projectId: 'prosapp-5747a', + databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com', storageBucket: 'prosapp-5747a.appspot.com', - androidClientId: '245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com', + androidClientId: '245204384533-04oabs744hp8fjbdreijp4h16nvfmeti.apps.googleusercontent.com', iosClientId: '245204384533-nv7e2n7d4vj5kfkc55mlmiiju0ch2qhn.apps.googleusercontent.com', iosBundleId: 'com.example.prosappco', ); static const FirebaseOptions macos = FirebaseOptions( apiKey: 'AIzaSyDUhnkNkeEPkqcwVo5EWr63q3oEX6bDGic', - appId: '1:245204384533:ios:aeb233e55c5ddf7a9f871c', + appId: '1:245204384533:ios:1d58f5624d0df4859f871c', messagingSenderId: '245204384533', projectId: 'prosapp-5747a', + databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com', storageBucket: 'prosapp-5747a.appspot.com', - androidClientId: '245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com', + androidClientId: '245204384533-04oabs744hp8fjbdreijp4h16nvfmeti.apps.googleusercontent.com', iosClientId: '245204384533-nv7e2n7d4vj5kfkc55mlmiiju0ch2qhn.apps.googleusercontent.com', iosBundleId: 'com.example.prosappco', ); diff --git a/lib/main.dart b/lib/main.dart index a544f8f..fb38fd1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:animated_splash_screen/animated_splash_screen.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; @@ -25,8 +25,9 @@ import 'package:prosappco/src/screens/solicitudes.dart'; import 'package:prosappco/src/services/local_notifications.dart'; import 'firebase_options.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter_web_plugins/url_strategy.dart'; +//import 'package:flutter_web_plugins/url_strategy.dart'; import 'package:flutter/foundation.dart' show kIsWeb; // Importa kIsWeb +//import 'dart:html' as html; Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); @@ -40,30 +41,28 @@ void main() async { options: DefaultFirebaseOptions.currentPlatform, ).then((value) => Get.put(AuthenticationRepository())); - FirebaseMessaging.onBackgroundMessage((_firebaseMessagingBackgroundHandler)); + // Evitar la solicitud de permiso en la versión web + if (!kIsWeb) { + FirebaseMessaging messaging = FirebaseMessaging.instance; - FirebaseMessaging messaging = FirebaseMessaging.instance; + NotificationSettings settings = await messaging.requestPermission( + alert: true, + announcement: false, + badge: true, + carPlay: false, + criticalAlert: false, + provisional: false, + sound: true, + ); - NotificationSettings settings = await messaging.requestPermission( - alert: true, - announcement: false, - badge: true, - carPlay: false, - criticalAlert: false, - provisional: false, - sound: true, - ); + print('User granted permission: ${settings.authorizationStatus}'); - FirebaseMessaging.onMessage.listen((RemoteMessage message) { - print('Got a message whilst in the foreground!'); - print('Message data: ${message.data}'); + FirebaseMessaging.onBackgroundMessage((_firebaseMessagingBackgroundHandler)); - if (message.notification != null) { - print('Message also contained a notification: ${message.notification}'); - } - }); - - print('User granted permission: ${settings.authorizationStatus}'); + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + // Tu código para manejar las notificaciones aquí + }); + } await initNotifications(); @@ -73,7 +72,7 @@ void main() async { DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); - usePathUrlStrategy(); + runApp(const MyApp()); } @@ -88,8 +87,8 @@ class MyApp extends StatelessWidget { title: 'ProsApp', initialRoute: '/', routes: { - '/splash': (context) => const SplashScreen(), '/': (context) => const LoginScreen(), + '/splash': (context) => const SplashScreen(), '/login': (context) => const LoginEmailScreen(), '/profile': (context) => const ProfileScreen(), '/register': (context) => const RegisterScreen(), diff --git a/lib/src/components/drawer_menu.dart b/lib/src/components/drawer_menu.dart index d8bf02b..652eeac 100644 --- a/lib/src/components/drawer_menu.dart +++ b/lib/src/components/drawer_menu.dart @@ -14,6 +14,7 @@ import 'package:prosappco/src/screens/profile.dart'; import 'package:prosappco/src/screens/profile_web.dart'; import 'package:prosappco/src/screens/reputation.dart'; import 'package:prosappco/src/screens/support.dart'; +import 'package:prosappco/src/screens/web_view.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:get/get.dart'; @@ -46,7 +47,7 @@ class _DrawerMenuState extends State { Future _irSugerencias() async { const url = - 'https://admin.prosapp.co/sugerencias '; // Aquí debes poner la URL a la que quieres dirigirte + 'https://admin.prosapp.co/sugerencias'; // Aquí debes poner la URL a la que quieres dirigirte if (await canLaunch(url)) { await launch(url); } else { @@ -224,7 +225,22 @@ class _DrawerMenuState extends State { ), ), ListTile( - onTap: _irSugerencias, + onTap: () { + if (kIsWeb) { + _irSugerencias(); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Sugerencias', + link: 'https://admin.prosapp.co/sugerencias'); + }, + ), + ); + } + }, leading: const Icon( Icons.campaign_outlined, color: Colors.black, @@ -390,14 +406,14 @@ class _DrawerMenuState extends State { user?.phoneNumber == null) { Get.snackbar( 'Completa tu perfil', - 'Para ser un profesional registrado, completa tu perfil. No olvides guardar tus cambios para que sean efectivos.', + 'Para ser un profesional registrado, asegúrate de llenar todos los campos necesarios y no olvides guardar tus cambios para que surtan efecto.', snackPosition: SnackPosition.BOTTOM, ); } else { if (currentUser?.phoneNumber != user?.phoneNumber) { Get.snackbar( - 'Número de teléfono sin cambios', - 'Tu número de teléfono no se ha actualizado. Para guardar los cambios, ve al apartado de perfil y presiona en "Guardar".', + 'Tu número de teléfono sigue sin cambios.', + 'Para guardar esta información, dirígete a tu perfil y selecciona la opción Guardar', snackPosition: SnackPosition.BOTTOM, ); } else { diff --git a/lib/src/components/drawer_professional.dart b/lib/src/components/drawer_professional.dart index 09700b5..fabc0c4 100644 --- a/lib/src/components/drawer_professional.dart +++ b/lib/src/components/drawer_professional.dart @@ -14,6 +14,7 @@ import 'package:prosappco/src/screens/profile.dart'; import 'package:prosappco/src/screens/profile_pro.dart'; import 'package:prosappco/src/screens/profile_pro_web.dart'; import 'package:prosappco/src/screens/reputacion_pro.dart'; +import 'package:prosappco/src/screens/web_view.dart'; import '../models/scores_model.dart'; import '../screens/configuracion.dart'; import '../screens/support.dart'; @@ -211,7 +212,22 @@ class _DrawerProfessionalState extends State { ), ), ListTile( - onTap: _irSugerencias, + onTap: () { + if (kIsWeb) { + _irSugerencias(); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Sugerencias', + link: 'https://admin.prosapp.co/sugerencias'); + }, + ), + ); + } + }, leading: const Icon( Icons.campaign_outlined, color: Colors.black, diff --git a/lib/src/components/primary_btn.dart b/lib/src/components/primary_btn.dart index 6c4d890..7197924 100644 --- a/lib/src/components/primary_btn.dart +++ b/lib/src/components/primary_btn.dart @@ -3,26 +3,40 @@ import 'package:flutter/material.dart'; class PrimaryButtom extends StatelessWidget { final VoidCallback onPressed; final String label; + final bool + isEnabled; // Nuevo parámetro para indicar si el botón está habilitado - const PrimaryButtom( - {super.key, required this.onPressed, required this.label}); + const PrimaryButtom({ + Key? key, + required this.onPressed, + required this.label, + this.isEnabled = true, // Valor predeterminado: habilitado + }) : super(key: key); @override Widget build(BuildContext context) { return ElevatedButton( - onPressed: onPressed, + onPressed: isEnabled + ? onPressed + : null, // Habilita/deshabilita el botón según isEnabled style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), + backgroundColor: isEnabled + ? const Color(0xFF2BA4EC) + : Colors.grey, // Cambia el color de fondo shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50), ), - elevation: 0, + elevation: isEnabled + ? 0 + : 0, // Cambia la elevación para dar una sensación de clickeabilidad minimumSize: const Size(230, 60), ), child: Text( label, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: isEnabled + ? Colors.white + : Colors.black, // Cambia el color del texto fontWeight: FontWeight.bold, fontSize: 18, ), diff --git a/lib/src/screens/calendar.dart b/lib/src/screens/calendar.dart index 12cf391..01b387c 100644 --- a/lib/src/screens/calendar.dart +++ b/lib/src/screens/calendar.dart @@ -170,7 +170,7 @@ class _CalendarScreenState extends State { content: StatefulBuilder( builder: (BuildContext context, StateSetter setStateDialog) { return SizedBox( - height: 350, + height: 800, child: Column( children: [ Padding( @@ -352,7 +352,7 @@ class _CalendarScreenState extends State { future: getByProId(DateFormat("yyyy-MM-dd 00:00:00.000").format(today)), builder: (BuildContext context, AsyncSnapshot> snapshot) { if (!snapshot.hasData) { - return const Text('No tiene citas'); + return const Text('No tienes citas'); } List eventos = []; diff --git a/lib/src/screens/cita.dart b/lib/src/screens/cita.dart index 1de66df..0fa8c78 100644 --- a/lib/src/screens/cita.dart +++ b/lib/src/screens/cita.dart @@ -71,7 +71,8 @@ class _CitaScreenState extends State { 'data': { 'click_action': 'FLUTTER_NOTIFICATION_CLICK', 'id': '1', - 'status': 'done' + 'status': 'done', + 'screen': 'misservicios', }, 'to': user }, @@ -233,45 +234,47 @@ class _CitaScreenState extends State { ], ), ), - 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), + 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: [ @@ -292,249 +295,259 @@ class _CitaScreenState extends State { style: const TextStyle( color: Colors.grey, fontStyle: FontStyle.italic), ), - widget.evento.status == 'terminado' - ? const SizedBox(height: 10) - : const Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: Text( - 'Medios de comunicación con el usuario.', - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - ), - widget.evento.status == 'terminado' - ? 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, - ), - ), - ), - ], - ) - : 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), - ), + widget.evento.userId == widget.evento.professionalId + ? const SizedBox() + : widget.evento.status == 'terminado' + ? const SizedBox(height: 10) + : const Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Text( + 'Medios de comunicación con el usuario.', + style: TextStyle(color: Color(0xFF2BA4EC)), ), ), - const SizedBox(width: 20), - ElevatedButton( - onPressed: () async { - if (widget.evento.status != 'pendiente') { - final chatDoc = FirebaseFirestore.instance - .collection('chats') - .doc(widget.evento.id); - final chatSnapshot = await chatDoc.get(); - - if (!chatSnapshot.exists || - chatSnapshot.data()!['message'] == null) { - await chatDoc.set( - { - 'professional_id': - widget.evento.professionalId, - 'user_id': widget.evento.userId, - 'message': [], - }, - SetOptions(merge: true), - ).catchError((error) => print( - 'Error al crear el documento: $error')); - } - - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ChatScreen( - eventoId: widget.evento.id); - }, - ), - ); - } - - // if (widget.evento.status != 'pendiente') { - // await FirebaseFirestore.instance - // .collection('chats') - // .doc(widget.evento.id) - // .set( - // { - // 'professional_id': - // widget.evento.professionalId, - // 'user_id': widget.evento.userId, - // 'message': [], - // }, - // SetOptions( - // merge: - // true)).catchError((error) => print( - // 'Error al crear el documento: $error')); - - // Navigator.push( - // context, - // CupertinoPageRoute( - // builder: (BuildContext context) { - // return ChatScreen( - // eventoId: widget.evento.id); - // }, - // ), - // ); - // } - else { - final snackBar = SnackBar( - backgroundColor: Colors.blue, - content: const Text( - 'Primero acepta la solicitud', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - elevation: 8.0, + widget.evento.userId == widget.evento.professionalId + ? const SizedBox() + : widget.evento.status == 'terminado' + ? 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, + ), + ), + ), + ], + ) + : 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(10.0), + 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 != 'pendiente') { + final chatDoc = FirebaseFirestore.instance + .collection('chats') + .doc(widget.evento.id); + final chatSnapshot = await chatDoc.get(); - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - style: ElevatedButton.styleFrom( - foregroundColor: const Color(0xFF2BA4EC), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, + if (!chatSnapshot.exists || + chatSnapshot.data()!['message'] == + null) { + await chatDoc.set( + { + 'professional_id': + widget.evento.professionalId, + 'user_id': widget.evento.userId, + 'message': [], + }, + SetOptions(merge: true), + ).catchError((error) => print( + 'Error al crear el documento: $error')); + } + + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ChatScreen( + eventoId: widget.evento.id); + }, + ), + ); + } + + // if (widget.evento.status != 'pendiente') { + // await FirebaseFirestore.instance + // .collection('chats') + // .doc(widget.evento.id) + // .set( + // { + // 'professional_id': + // widget.evento.professionalId, + // 'user_id': widget.evento.userId, + // 'message': [], + // }, + // SetOptions( + // merge: + // true)).catchError((error) => print( + // 'Error al crear el documento: $error')); + + // Navigator.push( + // context, + // CupertinoPageRoute( + // builder: (BuildContext context) { + // return ChatScreen( + // eventoId: widget.evento.id); + // }, + // ), + // ); + // } + else { + final snackBar = SnackBar( + backgroundColor: Colors.blue, + content: const Text( + 'Primero acepta la solicitud', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + elevation: 8.0, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10.0), + ), + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + 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), + ), ), ), - ), - 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, + 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), + ), ), ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 18, horizontal: 0), - child: Icon( - CommunityMaterialIcons.whatsapp, - size: 30, - color: Color(0xFF2BA4EC), - ), - ), + const Expanded(child: SizedBox()), + ], ), - const Expanded(child: SizedBox()), - ], - ), widget.evento.ubicacion == 'sitio' ? const SizedBox() : Padding( diff --git a/lib/src/screens/city.dart b/lib/src/screens/city.dart index 947bdac..8f0b752 100644 --- a/lib/src/screens/city.dart +++ b/lib/src/screens/city.dart @@ -152,7 +152,7 @@ class _CityScreenState extends State { onPressed: () { Navigator.pop(context); }, - label: 'Seleccione su ciudad'), + label: 'Selecciona tu ciudad'), body: Column( children: [ Padding( @@ -160,7 +160,7 @@ class _CityScreenState extends State { child: TextField( controller: searchController, decoration: const InputDecoration( - hintText: 'Buscar ciudad', + hintText: 'Busca una ciudad', prefixIcon: Icon(Icons.near_me), ), ), diff --git a/lib/src/screens/code_validation.dart b/lib/src/screens/code_validation.dart index 63bd297..3156591 100644 --- a/lib/src/screens/code_validation.dart +++ b/lib/src/screens/code_validation.dart @@ -40,7 +40,7 @@ class CodeValidationScreen extends StatelessWidget { }, ), const Text( - 'Validar código', + 'Valida el código', style: TextStyle( color: Color(0xFF262626), fontSize: 38.0, @@ -121,7 +121,7 @@ class CodeValidationScreen extends StatelessWidget { onPressed: () { OTPController.instance.verifyOTP(otp); }, - label: 'Validar código', + label: 'Valida el código', ), const SizedBox(height: 30), ], @@ -257,7 +257,7 @@ class CodeValidationScreen extends StatelessWidget { onPressed: () { OTPController.instance.verifyOTP(otp); }, - label: 'Validar código', + label: 'Valida el código', ), const SizedBox(height: 30), ], diff --git a/lib/src/screens/login.dart b/lib/src/screens/login.dart index 5266a71..d7d76d1 100644 --- a/lib/src/screens/login.dart +++ b/lib/src/screens/login.dart @@ -1,3 +1,5 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; @@ -5,8 +7,11 @@ import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/controllers/phone_auth_controller.dart'; +import 'package:prosappco/src/models/setting_model.dart'; import 'package:prosappco/src/screens/code_validation.dart'; +import 'package:prosappco/src/screens/web_view.dart'; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:url_launcher/url_launcher.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -19,6 +24,28 @@ class _LoginScreenState extends State { final controller = Get.put(PhoneAuthController()); final _formKey = GlobalKey(); String completePhoneNumber = ''; + bool _isChecked = false; + SettingModel? settings; + + void _launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url, forceSafariVC: false, forceWebView: false); + } else { + throw 'No se pudo abrir el enlace $url'; + } + } + + @override + void initState() { + super.initState(); + if (settings == null) { + SettingModel.getSettings().then( + (SettingModel value) => setState(() { + settings = value; + }), + ); + } + } @override Widget build(BuildContext context) { @@ -98,7 +125,49 @@ class _LoginScreenState extends State { color: Color(0xFF65676B), ), ), - const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Checkbox( + value: _isChecked, + onChanged: (value) { + setState(() { + _isChecked = value!; + }); + }, + ), + GestureDetector( + onTap: () { + if (kIsWeb) { + _launchURL(settings?.terminosCondiciones ?? ''); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Términos y condiciones', + link: settings?.terminosCondiciones ?? '', + ); + }, + ), + ); + } + }, + child: const Text( + 'Acepto los términos y condiciones.', + style: TextStyle( + fontSize: 13.0, + color: Color(0xFF65676B), + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ), PrimaryButtom( onPressed: () { if (_formKey.currentState!.validate()) { @@ -112,6 +181,7 @@ class _LoginScreenState extends State { ); } }, + isEnabled: _isChecked, label: 'Enviar código', ), const SizedBox(height: 20), @@ -216,14 +286,58 @@ class _LoginScreenState extends State { ), ), const Text( - 'Un código será enviado a este numero de celular.', + 'Se enviará un código a este número de celular.', textAlign: TextAlign.center, style: TextStyle( fontSize: 13.0, color: Color(0xFF65676B), ), ), - const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Checkbox( + value: _isChecked, + onChanged: (value) { + setState(() { + _isChecked = value!; + }); + }, + ), + GestureDetector( + onTap: () { + if (kIsWeb) { + _launchURL(settings?.terminosCondiciones ?? ''); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Términos y condiciones', + link: + settings?.terminosCondiciones ?? '', + ); + }, + ), + ); + } + }, + child: const Text( + 'Acepto los términos y condiciones.', + style: TextStyle( + fontSize: 13.0, + color: Color(0xFF65676B), + decoration: TextDecoration + .underline, // Add underline style + ), + ), + ), + ], + ), + ), PrimaryButtom( onPressed: () { if (_formKey.currentState!.validate()) { @@ -237,6 +351,7 @@ class _LoginScreenState extends State { ); } }, + isEnabled: _isChecked, label: 'Enviar código', ), const SizedBox(height: 20), @@ -266,7 +381,7 @@ class GestureBottom extends StatelessWidget { Navigator.pushNamed(context, '/login'); }, child: const Text( - 'Entrar con correo electronico', + 'Inicia sesión con tu correo electrónico', style: TextStyle( fontSize: 15.0, color: Color(0xFF65676B), decoration: TextDecoration.underline, // Subrayado @@ -291,14 +406,14 @@ class RichTxTBottom extends StatelessWidget { fontFamily: 'Poppins', ), children: [ - const TextSpan(text: 'No estas registrado? '), + const TextSpan(text: '¿No estás registrado? '), WidgetSpan( child: GestureDetector( onTap: () { Navigator.pushNamed(context, '/register'); }, child: const Text( - 'Registrarse', + 'Regístrate', style: TextStyle( fontSize: 16.0, color: Color(0xFF2BA4EC), diff --git a/lib/src/screens/login_email.dart b/lib/src/screens/login_email.dart index d6048cd..e12cec4 100644 --- a/lib/src/screens/login_email.dart +++ b/lib/src/screens/login_email.dart @@ -114,7 +114,7 @@ class _LoginEmailScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'Entrar con Google ', + 'Entra con Google ', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -179,7 +179,7 @@ class _LoginEmailScreenState extends State { obscureText: _obscureText, validator: (value) { if (value == null || value.isEmpty) { - return 'Por favor ingrese una contraseña'; + return 'Por favor, ingresa una contraseña'; } return null; }, @@ -383,7 +383,7 @@ class _LoginEmailScreenState extends State { obscureText: _obscureText, validator: (value) { if (value == null || value.isEmpty) { - return 'Por favor ingrese una contraseña'; + return 'Por favor, ingresa una contraseña'; } return null; }, @@ -466,12 +466,12 @@ class FormEmail extends StatelessWidget { controller: controller.email, validator: (String? value) { if (value == null || value.isEmpty) { - return 'Por favor ingrese un email'; + return 'Por favor, ingresa un Email'; } final RegExp emailRegExp = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingrese un email válido'; + return 'Por favor, ingresa un Email válido'; } return null; }, @@ -551,7 +551,7 @@ class RichTxtBottom extends StatelessWidget { fontFamily: 'Poppins', ), children: [ - const TextSpan(text: 'No estas registrado? '), + const TextSpan(text: '¿No estás registrado? '), WidgetSpan( child: GestureDetector( onTap: () { diff --git a/lib/src/screens/messages.dart b/lib/src/screens/messages.dart index dd58caa..e4fea93 100644 --- a/lib/src/screens/messages.dart +++ b/lib/src/screens/messages.dart @@ -44,7 +44,7 @@ class _MessagesScreenState extends State { child: TextField( // controller: searchController, decoration: InputDecoration( - hintText: 'Escriba un nombre', + hintText: 'Escribe un nombre', prefixIcon: Icon(CommunityMaterialIcons.stethoscope), ), ), diff --git a/lib/src/screens/messages_user.dart b/lib/src/screens/messages_user.dart index 1ee15de..e8cbc23 100644 --- a/lib/src/screens/messages_user.dart +++ b/lib/src/screens/messages_user.dart @@ -45,7 +45,7 @@ class _MessagesUserScreenState extends State { child: TextField( // controller: searchController, decoration: InputDecoration( - hintText: 'Escriba un nombre', + hintText: 'Escribe un nombre', prefixIcon: Icon(CommunityMaterialIcons.stethoscope), ), ), diff --git a/lib/src/screens/my_services.dart b/lib/src/screens/my_services.dart index 8b1da12..adbb692 100644 --- a/lib/src/screens/my_services.dart +++ b/lib/src/screens/my_services.dart @@ -87,6 +87,7 @@ class _MyServicesScreenState extends State { try { eventos.addAll(snapshot.data!); + eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1)); } catch (e) { print("Error" + e.toString()); } @@ -100,108 +101,137 @@ class _MyServicesScreenState extends State { return Column( children: [ - ...eventos.map( - (event) => ListTile( - tileColor: event.status == 'denegado' - ? Colors.red[100] - : Colors.blue[100], - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - }, - leading: event.status == 'aprobado' - ? const Icon( - Icons.check, - color: Colors.blue, - size: 40, - ) - : event.status == 'iniciado' - ? const Icon( - Icons.access_time, - color: Colors.blue, - size: 40, - ) - : event.status == 'pendiente' + ...eventos + .where((event) => event.professionalId != event.userId) + .map( + (event) => FutureBuilder( + future: FirebaseFirestore.instance + .collection('users') + .doc(event.professionalId) + .get(), + builder: (BuildContext context, + AsyncSnapshot profSnapshot) { + if (profSnapshot.connectionState == + ConnectionState.waiting) { + return const CircularProgressIndicator(); + } + + if (profSnapshot.hasError) { + return const Text( + 'Error al obtener los datos del profesional', + // Aquí puedes agregar un estilo de texto apropiado + ); + } + + final professionalData = profSnapshot.data; + final professionalName = + professionalData?['name'] ?? 'N/D'; + + return ListTile( + tileColor: event.status == 'denegado' + ? Colors.red[100] + : Colors.blue[100], + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); + }, + ), + ); + }, + leading: event.status == 'aprobado' ? const Icon( - Icons.access_time_outlined, + Icons.check, color: Colors.blue, size: 40, ) - : event.status == 'terminado' + : event.status == 'iniciado' ? const Icon( - Icons.rocket_launch, + Icons.access_time, color: Colors.blue, size: 40, ) - : const Icon( - Icons.close, - color: Colors.red, - size: 40, + : event.status == 'pendiente' + ? const Icon( + Icons.access_time_outlined, + color: Colors.blue, + size: 40, + ) + : event.status == 'terminado' + ? const Icon( + Icons.rocket_launch, + color: Colors.blue, + size: 40, + ) + : const Icon( + Icons.close, + color: Colors.red, + size: 40, + ), + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${professionalName}, ', + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + TextSpan( + text: + '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}', + style: TextStyle( + color: Colors.grey[600], + fontSize: 16, + ), + ), + ], + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + RatingBar.builder( + initialRating: + event.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), ), - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${event.title}, ', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, + onRatingUpdate: (rating) {}, + ignoreGestures: true, + ), + const SizedBox(width: 5), + Text( + '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), + ], + ), + Text( + '" ${event.description}"', + style: + const TextStyle(fontStyle: FontStyle.italic), + ), + ], ), - ), - TextSpan( - text: - '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}', - style: TextStyle( - color: Colors.grey[600], - fontSize: 16, - ), - ), - ], + trailing: const Icon(Icons.keyboard_arrow_right), + ); + }, ), ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: event.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( - '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '" ${event.description}"', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ], - ), - trailing: const Icon(Icons.keyboard_arrow_right), - ), - ), ], ); }, diff --git a/lib/src/screens/my_services_pro.dart b/lib/src/screens/my_services_pro.dart index acf1e3e..5450dd7 100644 --- a/lib/src/screens/my_services_pro.dart +++ b/lib/src/screens/my_services_pro.dart @@ -31,10 +31,11 @@ class _MyServicesProScreenState extends State { return SafeArea( child: Scaffold( appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Mis servicios'), + onPressed: () { + Navigator.pop(context); + }, + label: 'Mis servicios', + ), drawer: DrawerProfessional(), body: SingleChildScrollView( child: Column( @@ -82,6 +83,7 @@ class _MyServicesProScreenState extends State { try { eventos.addAll(snapshot.data!); + eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1)); } catch (e) { print("Error" + e.toString()); } @@ -96,102 +98,100 @@ class _MyServicesProScreenState extends State { return Column( children: [ ...eventos.map( - (event) => event.userId == event.professionalId - ? const SizedBox() - : ListTile( - tileColor: event.status == 'denegado' - ? Colors.red[100] - : Colors.blue[100], - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); + (event) => ListTile( + tileColor: event.status == 'denegado' + ? Colors.red[100] + : Colors.blue[100], + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); }, - leading: event.status == 'aprobado' - ? const Icon( - Icons.check, - color: Colors.blue, - size: 40, - ) - : event.status == 'iniciado' - ? const Icon( - Icons.access_time, - color: Colors.blue, - size: 40, - ) - : event.status == 'terminado' - ? const Icon( - Icons.rocket_launch, - color: Colors.blue, - size: 40, - ) - : const Icon( - Icons.close, - color: Colors.red, - size: 40, - ), - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${event.title}, ', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, + ), + ); + }, + leading: event.status == 'aprobado' + ? const Icon( + Icons.check, + color: Colors.blue, + size: 40, + ) + : event.status == 'iniciado' + ? const Icon( + Icons.access_time, + color: Colors.blue, + size: 40, + ) + : event.status == 'terminado' + ? const Icon( + Icons.rocket_launch, + color: Colors.blue, + size: 40, + ) + : const Icon( + Icons.close, + color: Colors.red, + size: 40, ), - ), - TextSpan( - text: - '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}', - style: TextStyle( - color: Colors.grey[600], - fontSize: 16, - ), - ), - ], + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${event.title}, ', + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16, ), ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: event.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( - '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '" ${event.description}"', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ], + TextSpan( + text: + '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}', + style: TextStyle( + color: Colors.grey[600], + fontSize: 16, + ), ), - trailing: const Icon(Icons.keyboard_arrow_right), + ], + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + RatingBar.builder( + initialRating: event.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( + '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), + ], ), + Text( + '" ${event.description}"', + style: const TextStyle(fontStyle: FontStyle.italic), + ), + ], + ), + trailing: const Icon(Icons.keyboard_arrow_right), + ), ), ], ); diff --git a/lib/src/screens/new_number.dart b/lib/src/screens/new_number.dart index 12a469b..d415122 100644 --- a/lib/src/screens/new_number.dart +++ b/lib/src/screens/new_number.dart @@ -102,7 +102,7 @@ class _NewNumberScreenState extends State { const Padding( padding: EdgeInsets.only(bottom: 30), child: Text( - 'Un código será enviado a este numero de celular.', + 'Se enviará un código a este número de celular', textAlign: TextAlign.center, style: TextStyle( fontSize: 13.0, color: Color(0xFF65676B))), @@ -215,7 +215,7 @@ class _NewNumberScreenState extends State { const Padding( padding: EdgeInsets.only(bottom: 30), child: Text( - 'Un código será enviado a este numero de celular.', + 'Se enviará un código a este número de celular', textAlign: TextAlign.center, style: TextStyle( fontSize: 13.0, color: Color(0xFF65676B))), diff --git a/lib/src/screens/new_number_validation.dart b/lib/src/screens/new_number_validation.dart index e8f7f0e..1332a71 100644 --- a/lib/src/screens/new_number_validation.dart +++ b/lib/src/screens/new_number_validation.dart @@ -49,7 +49,7 @@ class _NewNumberValidationScreenState extends State { }, ), const Text( - 'Validar código', + 'Valida el código', style: TextStyle( color: Color(0xFF262626), fontSize: 38.0, @@ -98,7 +98,7 @@ class _NewNumberValidationScreenState extends State { alignment: Alignment.topLeft, child: Padding( padding: EdgeInsets.only(bottom: 5), - child: Text('Codigo', + child: Text('Código', textAlign: TextAlign.left, style: TextStyle(fontSize: 18.0, color: Color(0xFF65676B))), @@ -135,7 +135,7 @@ class _NewNumberValidationScreenState extends State { minimumSize: const Size(230, 60), // Tamaño mínimo del botón ), child: const Text( - 'Validar código', + 'Valida el código', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, diff --git a/lib/src/screens/new_password.dart b/lib/src/screens/new_password.dart index 928260a..bef584b 100644 --- a/lib/src/screens/new_password.dart +++ b/lib/src/screens/new_password.dart @@ -32,7 +32,7 @@ class _NewPasswordScreenState extends State { if (newPassword == currentPassword) { Get.snackbar( 'Misma contraseña', - 'La nueva contraseña debe ser diferente a la contraseña actual.', + 'La nueva contraseña debe ser distinta a la contraseña actual.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -50,7 +50,7 @@ class _NewPasswordScreenState extends State { // Muestra un mensaje de éxito Get.snackbar( 'Contraseña actualizada', - 'La contraseña ha sido actualizada correctamente.', + 'Tu contraseña ha sido cambiada con éxito.', snackPosition: SnackPosition.BOTTOM, ); } catch (e) { @@ -60,7 +60,7 @@ class _NewPasswordScreenState extends State { } catch (e) { Get.snackbar( 'Contraseña incorrecta', - 'Error al actualizar la contraseña asegurese de ingresar bien la contraseña actual.', + 'Ha ocurrido un error al actualizar la contraseña. Asegúrate de ingresar correctamente la contraseña actual.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -80,7 +80,7 @@ class _NewPasswordScreenState extends State { onPressed: () { Navigator.pop(context); }, - label: 'Cambie su contraseña', + label: ' Cambia tu contraseña', ), body: Center( child: SizedBox( diff --git a/lib/src/screens/profession.dart b/lib/src/screens/profession.dart index 07be18d..23fe04d 100644 --- a/lib/src/screens/profession.dart +++ b/lib/src/screens/profession.dart @@ -138,7 +138,7 @@ class _ProfessionScreenState extends State { child: TextField( controller: searchController, decoration: const InputDecoration( - hintText: 'Buscar su profesión', + hintText: 'Busca tu profesión', prefixIcon: Icon(Icons.assignment_ind_rounded), ), ), diff --git a/lib/src/screens/professional_direccion.dart b/lib/src/screens/professional_direccion.dart index e016181..329a855 100644 --- a/lib/src/screens/professional_direccion.dart +++ b/lib/src/screens/professional_direccion.dart @@ -206,7 +206,7 @@ class _ProfessionalDireccionScreenState setState(() {}); } catch (e) { Get.snackbar( - 'ubicación desactivada', + 'Ubicación desactivada', 'Por favor activa la ubicacion de tu telefono.', snackPosition: SnackPosition.TOP, ); diff --git a/lib/src/screens/professional_profile.dart b/lib/src/screens/professional_profile.dart index b8914e8..6dd4b53 100644 --- a/lib/src/screens/professional_profile.dart +++ b/lib/src/screens/professional_profile.dart @@ -422,7 +422,8 @@ class ProfessionalProfileScreenState extends State { especializacion.split(',').map((e) => e.trim()).toList(); if (cedula.isEmpty) { - _showCustomSnackBar(context, 'Por favor, ingrese el número de cédula.'); + _showCustomSnackBar( + context, 'Por favor, adjunta el documento PDF de tu cédula.'); return; } @@ -434,7 +435,7 @@ class ProfessionalProfileScreenState extends State { if (image_certificado == null) { _showCustomSnackBar(context, - 'Por favor, adjunte el documento PDF de su certificado. ¡Gracias por su colaboración!'); + 'Por favor, adjunta el documento PDF de tu certificado. ¡Gracias por tu colaboración!'); return; } @@ -770,7 +771,7 @@ class ProfessionalProfileScreenState extends State { SizedBox(width: 15), Expanded( child: Text( - '¡Importante! Solo aceptamos archivos en formato PDF. Si desea agregar más de una especialidad, envíe archivos adicionales separados por comas (,). ¡Gracias!', + 'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!', style: TextStyle(color: Colors.black, fontSize: 14), ), ) diff --git a/lib/src/screens/professional_profile_web.dart b/lib/src/screens/professional_profile_web.dart index 2b84b48..9f83e78 100644 --- a/lib/src/screens/professional_profile_web.dart +++ b/lib/src/screens/professional_profile_web.dart @@ -645,7 +645,7 @@ class _ProfessionalProfileWebScreenState SizedBox(width: 15), Expanded( child: Text( - 'Para añadir mas de una especialidad, envie mas fotos y separe por comas (,).', + 'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!', style: TextStyle( color: Colors.black, fontSize: 14), ), diff --git a/lib/src/screens/professional_revision.dart b/lib/src/screens/professional_revision.dart index 9727353..b24b823 100644 --- a/lib/src/screens/professional_revision.dart +++ b/lib/src/screens/professional_revision.dart @@ -71,7 +71,7 @@ class _ProfessionalRevisionScreenState children: [ Expanded( child: Text( - 'Gracias por proporcionar su información. Actualmente, estamos revisando sus datos y una vez aprobados, podrá acceder al perfil profesional sin problemas. Le notificaremos tan pronto como su cuenta esté lista. ¡Gracias por su paciencia!.', + 'Gracias por proporcionar tu información. Actualmente, estamos revisando tus datos y una vez aprobados, podrás acceder al perfil profesional sin problemas. Te notificaremos tan pronto como tu cuenta esté lista. ¡Gracias por tu paciencia!', style: TextStyle( fontSize: 14, ), diff --git a/lib/src/screens/profile.dart b/lib/src/screens/profile.dart index 08abba6..53d4447 100644 --- a/lib/src/screens/profile.dart +++ b/lib/src/screens/profile.dart @@ -86,7 +86,6 @@ class _ProfileScreenState extends State { .collection('users') .doc(uid) .update({'photo': image}); - } catch (e) { try { await FirebaseFirestore.instance @@ -222,7 +221,7 @@ class _ProfileScreenState extends State { // Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario Get.snackbar( 'Nombre Invalido', - 'Ingrese un nombre válido.', + 'Ingresa un nombre válido.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -232,7 +231,7 @@ class _ProfileScreenState extends State { // Si el nuevo email está vacío, no lo actualizamos y mostramos un mensaje al usuario Get.snackbar( 'Correo Invalido', - 'Ingrese un email válido.', + 'Ingresa un email válido.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -259,7 +258,7 @@ class _ProfileScreenState extends State { } else { Get.snackbar( 'Contraseña Invalida', - 'Porfavor ingrese una contraseña.', + 'Porfavor ingresa una contraseña.', snackPosition: SnackPosition.BOTTOM, ); } @@ -305,7 +304,7 @@ class _ProfileScreenState extends State { } catch (e) { Get.snackbar( 'Agregar correo', - 'Inicie sesión para asegurarnos de que seas tú .', + 'Inicia sesión para asegurarnos de que seas tú.', snackPosition: SnackPosition.BOTTOM, ); AuthenticationRepository.instance.logout(uid!); diff --git a/lib/src/screens/profile_pro.dart b/lib/src/screens/profile_pro.dart index 133e416..ced044f 100644 --- a/lib/src/screens/profile_pro.dart +++ b/lib/src/screens/profile_pro.dart @@ -276,7 +276,7 @@ class _ProfileProScreenState extends State { Get.snackbar( 'Información actualizada', - 'Su información a sido actualizada con exito.', + 'Tu información ha sido actualizada con éxito.', snackPosition: SnackPosition.TOP, ); diff --git a/lib/src/screens/profile_pro_web.dart b/lib/src/screens/profile_pro_web.dart index 0b045d6..cd7cfdc 100644 --- a/lib/src/screens/profile_pro_web.dart +++ b/lib/src/screens/profile_pro_web.dart @@ -178,7 +178,7 @@ class _ProfileProWebScreenState extends State { Get.snackbar( 'Información actualizada', - 'Su información a sido actualizada con exito.', + 'Tu información a sido actualizada con exito.', snackPosition: SnackPosition.TOP, ); diff --git a/lib/src/screens/profile_web.dart b/lib/src/screens/profile_web.dart index 59a94e8..86841eb 100644 --- a/lib/src/screens/profile_web.dart +++ b/lib/src/screens/profile_web.dart @@ -142,7 +142,7 @@ class _ProfileWebScreenState extends State { // Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario Get.snackbar( 'Nombre Invalido', - 'Ingrese un nombre válido.', + 'Ingresa un nombre válido.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -161,7 +161,7 @@ class _ProfileWebScreenState extends State { // Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario Get.snackbar( 'Correo Invalido', - 'Ingrese un email válido.', + 'Ingresa un email válido.', snackPosition: SnackPosition.BOTTOM, ); return; @@ -186,7 +186,7 @@ class _ProfileWebScreenState extends State { } else { Get.snackbar( 'Contraseña Invalida', - 'Porfavor ingrese una contraseña.', + 'Por favor, ingresa una contraseña.', snackPosition: SnackPosition.BOTTOM, ); } @@ -262,7 +262,7 @@ class _ProfileWebScreenState extends State { } catch (e) { Get.snackbar( 'Agregar correo', - 'Inicie sesión para asegurarnos de que seas tú .', + 'Inicia sesión para asegurarnos de que seas tú.', snackPosition: SnackPosition.BOTTOM, ); AuthenticationRepository.instance.logout(uid!); diff --git a/lib/src/screens/register.dart b/lib/src/screens/register.dart index deb2158..4d6ee32 100644 --- a/lib/src/screens/register.dart +++ b/lib/src/screens/register.dart @@ -1,4 +1,5 @@ import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -7,8 +8,11 @@ import 'package:prosappco/main.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/controllers/register_controller.dart'; +import 'package:prosappco/src/models/setting_model.dart'; import 'package:prosappco/src/screens/login.dart'; +import 'package:prosappco/src/screens/web_view.dart'; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:url_launcher/url_launcher.dart'; class RegisterScreen extends StatefulWidget { const RegisterScreen({super.key}); @@ -21,6 +25,28 @@ class _RegisterScreenState extends State { bool _obscureText = true; final controller = Get.put(RegisterController()); final _formKey = GlobalKey(); + bool _isChecked = false; + SettingModel? settings; + + void _launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url, forceSafariVC: false, forceWebView: false); + } else { + throw 'No se pudo abrir el enlace $url'; + } + } + + @override + void initState() { + super.initState(); + if (settings == null) { + SettingModel.getSettings().then( + (SettingModel value) => setState(() { + settings = value; + }), + ); + } + } @override Widget build(BuildContext context) { @@ -157,138 +183,184 @@ class _RegisterScreenState extends State { ) : const SizedBox(), Form( - key: _formKey, - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 18), - child: TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese un email'; - } - final RegExp emailRegExp = RegExp( - r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingrese un email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB( - 255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Hello@gmail.com', - fillColor: - Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: Icon(Icons.email_outlined)), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese una contraseña'; - } - if (value.length <= 6) { - return 'Contraseña muy corta'; - } - return null; - }, - decoration: InputDecoration( - errorBorder: const OutlineInputBorder( + key: _formKey, + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 5), + child: Align( + alignment: Alignment.topLeft, + child: Text('Email', + style: TextStyle( + fontSize: 18.0, + color: Color(0xFF65676B))), + )), + Padding( + padding: const EdgeInsets.only(bottom: 18), + child: TextFormField( + controller: controller.email, + validator: (String? value) { + if (value == null || value.isEmpty) { + return 'Por favor ingresa un email'; + } + final RegExp emailRegExp = RegExp( + r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegExp.hasMatch(value)) { + return 'Por favor ingresa un email válido'; + } + return null; + }, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + errorBorder: OutlineInputBorder( borderSide: BorderSide( color: Color.fromARGB(255, 184, 0, 0)), borderRadius: BorderRadius.all( Radius.circular(50), )), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - border: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Contraseña', - fillColor: const Color.fromARGB( - 255, 239, 239, 239), + hintText: 'Hello@gmail.com', + fillColor: + Color.fromARGB(255, 239, 239, 239), filled: true, - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, + prefixIcon: Icon(Icons.email_outlined)), + ), + ), + const Padding( + padding: EdgeInsets.only(bottom: 5), + child: Align( + alignment: Alignment.topLeft, + child: Text('Password', + style: TextStyle( + fontSize: 18.0, + color: Color(0xFF65676B))), + )), + Padding( + padding: const EdgeInsets.only(bottom: 25), + child: TextFormField( + controller: controller.password, + obscureText: _obscureText, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor ingresa una contraseña'; + } + if (value.length <= 6) { + return 'Contraseña muy corta'; + } + return null; + }, + decoration: InputDecoration( + errorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: + Color.fromARGB(255, 184, 0, 0)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + enabledBorder: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + border: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + focusedBorder: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + hintText: 'Contraseña', + fillColor: + const Color.fromARGB(255, 239, 239, 239), + filled: true, + prefixIcon: const Icon(Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility + : Icons.visibility_off, + color: Colors.grey, ), + onPressed: () { + setState(() { + _obscureText = !_obscureText; + }); + }, ), ), ), - ], - )), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment + .center, // Centra horizontalmente + children: [ + Checkbox( + value: _isChecked, + onChanged: (value) { + setState(() { + _isChecked = value!; + }); + }, + ), + GestureDetector( + onTap: () { + if (kIsWeb) { + _launchURL( + settings?.terminosCondiciones ?? ''); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Políticas de privacidad', + link: settings?.terminosCondiciones ?? + '', + ); + }, + ), + ); + } + }, + child: const Text( + 'Acepto los términos y condiciones.', + style: TextStyle( + color: Color(0xFF65676B), + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ), Padding( padding: const EdgeInsets.only(bottom: 25), child: Center( @@ -302,6 +374,7 @@ class _RegisterScreenState extends State { } }, label: 'Registrarme', + isEnabled: _isChecked, ), ), ), @@ -407,195 +480,187 @@ class _RegisterScreenState extends State { horizontal: 0, vertical: 20), child: Column( children: [ - // Padding( - // padding: const EdgeInsets.only(bottom: 20), - // child: ElevatedButton( - // onPressed: () async { - // await AuthenticationRepository.instance - // .signInWithGoogle(); - // }, - // style: ElevatedButton.styleFrom( - // backgroundColor: const Color(0xFF2BA4EC), - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(50), - // ), - // elevation: 0, - // minimumSize: const Size(230, 60), - // ), - // child: const Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Text( - // 'Entrar con Google ', - // style: TextStyle( - // color: Colors.white, - // fontWeight: FontWeight.bold, - // fontSize: 18, - // ), - // ), - // SizedBox(width: 5), - // FaIcon(FontAwesomeIcons.google), - // ], - // )), - // ), - // const Padding( - // padding: EdgeInsets.symmetric(vertical: 0), - // child: Row( - // children: [ - // Expanded( - // child: Divider( - // color: Colors.black38, - // thickness: 1, - // ), - // ), - // Padding( - // padding: EdgeInsets.symmetric(horizontal: 10), - // child: Text("ó"), - // ), - // Expanded( - // child: Divider( - // color: Colors.black38, - // thickness: 1, - // ), - // ), - // ], - // ), - // ), Form( - key: _formKey, - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 18), - child: TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese un email'; - } - final RegExp emailRegExp = RegExp( - r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingrese un email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB( - 255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Hello@gmail.com', - fillColor: Color.fromARGB( - 255, 239, 239, 239), - filled: true, - prefixIcon: - Icon(Icons.email_outlined)), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese una contraseña'; - } - if (value.length <= 6) { - return 'Contraseña muy corta'; - } - return null; - }, - decoration: InputDecoration( - errorBorder: const OutlineInputBorder( + key: _formKey, + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 5), + child: Align( + alignment: Alignment.topLeft, + child: Text('Email', + style: TextStyle( + fontSize: 18.0, + color: Color(0xFF65676B))), + )), + Padding( + padding: const EdgeInsets.only(bottom: 18), + child: TextFormField( + controller: controller.email, + validator: (String? value) { + if (value == null || value.isEmpty) { + return 'Por favor ingresa un email'; + } + final RegExp emailRegExp = RegExp( + r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegExp.hasMatch(value)) { + return 'Por favor ingresa un email válido'; + } + return null; + }, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + errorBorder: OutlineInputBorder( borderSide: BorderSide( color: Color.fromARGB( 255, 184, 0, 0)), borderRadius: BorderRadius.all( Radius.circular(50), )), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - border: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Contraseña', - fillColor: const Color.fromARGB( - 255, 239, 239, 239), + hintText: 'Hello@gmail.com', + fillColor: + Color.fromARGB(255, 239, 239, 239), filled: true, - prefixIcon: - const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, + prefixIcon: Icon(Icons.email_outlined)), + ), + ), + const Padding( + padding: EdgeInsets.only(bottom: 5), + child: Align( + alignment: Alignment.topLeft, + child: Text('Password', + style: TextStyle( + fontSize: 18.0, + color: Color(0xFF65676B))), + )), + Padding( + padding: const EdgeInsets.only(bottom: 25), + child: TextFormField( + controller: controller.password, + obscureText: _obscureText, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor ingresa una contraseña'; + } + if (value.length <= 6) { + return 'Contraseña muy corta'; + } + return null; + }, + decoration: InputDecoration( + errorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Color.fromARGB( + 255, 184, 0, 0)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + border: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFECECEC)), + borderRadius: BorderRadius.all( + Radius.circular(50), + )), + hintText: 'Contraseña', + fillColor: const Color.fromARGB( + 255, 239, 239, 239), + filled: true, + prefixIcon: + const Icon(Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility + : Icons.visibility_off, + color: Colors.grey, ), + onPressed: () { + setState(() { + _obscureText = !_obscureText; + }); + }, ), ), ), - ], - )), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment + .center, // Centra horizontalmente + children: [ + Checkbox( + value: _isChecked, + onChanged: (value) { + setState(() { + _isChecked = value!; + }); + }, + ), + GestureDetector( + onTap: () { + if (kIsWeb) { + _launchURL( + settings?.terminosCondiciones ?? ''); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Políticas de privacidad', + link: settings + ?.terminosCondiciones ?? + '', + ); + }, + ), + ); + } + }, + child: const Text( + 'Acepto los términos y condiciones.', + style: TextStyle( + color: Color(0xFF65676B), + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ), Padding( padding: const EdgeInsets.only(bottom: 25), child: Center( @@ -608,6 +673,7 @@ class _RegisterScreenState extends State { ); } }, + isEnabled: _isChecked, label: 'Registrarme', ), ), diff --git a/lib/src/screens/service.dart b/lib/src/screens/service.dart index ae2abd0..6adef7b 100644 --- a/lib/src/screens/service.dart +++ b/lib/src/screens/service.dart @@ -14,12 +14,14 @@ import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/drawer_menu.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/user_model.dart'; +import 'package:prosappco/src/screens/my_services.dart'; import 'package:prosappco/src/screens/professional.dart'; import 'package:intl/intl.dart'; import 'package:prosappco/src/screens/profile.dart'; import 'package:prosappco/src/screens/profile_web.dart'; import 'package:prosappco/src/screens/service_after.dart'; import 'package:prosappco/src/screens/service_type.dart'; +import 'package:prosappco/src/screens/solicitudes.dart'; import 'package:prosappco/src/screens/ubicacion.dart'; import 'package:http/http.dart' as http; import 'package:prosappco/src/components/network_utility.dart'; @@ -66,7 +68,8 @@ class _ServiceScreenState extends State { 'data': { 'click_action': 'FLUTTER_NOTIFICATION_CLICK', 'id': '1', - 'status': 'done' + 'status': 'done', + 'screen': 'solicitud' }, 'to': pro }, @@ -596,515 +599,496 @@ class _ServiceScreenState extends State { child: Scaffold( backgroundColor: const Color(0xFFD6F4FF), drawer: DrawerMenu(), - body: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - Stack( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.6, - child: GoogleMap( - mapType: MapType.normal, - initialCameraPosition: initialCameraPosition, - markers: markers, - zoomControlsEnabled: false, - onMapCreated: (GoogleMapController controller) { - googleMapController = controller; - }, - onCameraIdle: () { - if (professionalAddress == '') { - if (coordinates != null) { - getLocationName( - coordinates.latitude, coordinates.longitude) - .then((locationName) { - setState(() { - _locationController.text = locationName; - }); + body: Column( + children: [ + Stack( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.5, + child: GoogleMap( + mapType: MapType.normal, + initialCameraPosition: initialCameraPosition, + markers: markers, + zoomControlsEnabled: false, + onMapCreated: (GoogleMapController controller) { + googleMapController = controller; + }, + onCameraIdle: () { + if (professionalAddress == '') { + if (coordinates != null) { + getLocationName( + coordinates.latitude, coordinates.longitude) + .then((locationName) { + setState(() { + _locationController.text = locationName; }); - } + }); } - }, - onCameraMove: (position) { - setState(() { - coordinates = position.target; - }); - }, - gestureRecognizers: >{ - Factory( - () => EagerGestureRecognizer(), - ), - }, - ), - ), - const Positioned( - bottom: 25, - right: 0, - left: 0, - top: 0, - child: Icon( - Icons.location_on, - size: 40, - color: Color(0xFFFF0000), - ), - ), - Positioned( - bottom: 10, - right: 20, - child: FloatingActionButton( - onPressed: () async { - try { - Position position = await _determinePosition(); - - googleMapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: LatLng( - position.latitude, - position.longitude, - ), - zoom: 17), - ), - ); - setState(() {}); - } catch (e) { - Get.snackbar( - 'ubicación desactivada', - 'Por favor activa la ubicacion de tu telefono.', - snackPosition: SnackPosition.TOP, - ); - } - - // markers.clear(); - }, - elevation: 0, - child: const Icon( - Icons.gps_fixed, - size: 30, + } + }, + onCameraMove: (position) { + setState(() { + coordinates = position.target; + }); + }, + gestureRecognizers: >{ + Factory( + () => EagerGestureRecognizer(), ), - ), + }, ), - Padding( - padding: const EdgeInsets.all(20.0), - child: Builder( - builder: (BuildContext context) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - shape: const CircleBorder(), - elevation: 3, - minimumSize: const Size(50, 50), + ), + const Positioned( + bottom: 25, + right: 0, + left: 0, + top: 0, + child: Icon( + Icons.location_on, + size: 40, + color: Color(0xFFFF0000), + ), + ), + Positioned( + bottom: 10, + right: 20, + child: FloatingActionButton( + onPressed: () async { + try { + Position position = await _determinePosition(); + + googleMapController.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng( + position.latitude, + position.longitude, + ), + zoom: 17), ), - child: const Icon( - Icons.menu, - color: Colors.black, - size: 30, - ), - onPressed: () { - Scaffold.of(context).openDrawer(); - }, ); - }, + setState(() {}); + } catch (e) { + Get.snackbar( + 'ubicación desactivada', + 'Por favor activa la ubicacion de tu telefono.', + snackPosition: SnackPosition.TOP, + ); + } + + // markers.clear(); + }, + elevation: 0, + child: const Icon( + Icons.gps_fixed, + size: 30, ), ), - Padding( - padding: - const EdgeInsets.only(top: 22, left: 100, right: 25), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(50), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.4), - spreadRadius: 1, - blurRadius: 2, - offset: const Offset(1, 2), - ), - ], - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - onTap: () async { - final String? serviceType = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ServiceTypeScreen(); - }, - ), - ) as String?; - - if (serviceType != null) { - setState(() { - _serviceType = serviceType; - _serviceTypeController.text = _serviceType; - }); - } - }, - controller: _serviceTypeController, - style: const TextStyle( - color: Colors.black87, - fontWeight: FontWeight.w600, - ), - readOnly: true, - decoration: InputDecoration( - hintText: - _serviceType == '' ? 'Servicio' : _serviceType, - hintStyle: const TextStyle( - color: Colors.black87, - fontWeight: FontWeight.w600), - border: InputBorder.none, - enabledBorder: InputBorder.none, - ), + ), + Padding( + padding: const EdgeInsets.all(20.0), + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: const CircleBorder(), + elevation: 3, + minimumSize: const Size(50, 50), ), - ), - ), + child: const Icon( + Icons.menu, + color: Colors.black, + size: 30, + ), + onPressed: () { + Scaffold.of(context).openDrawer(); + }, + ); + }, ), - ], - ), - Container( - width: double.infinity, - decoration: const BoxDecoration(color: Colors.white), - child: Column( - children: [ - const SizedBox(height: 5), - SizedBox( - width: 320, - child: Form( - child: Column( - children: [ - Column( - children: [ - TextFormField( - controller: _profesionalController, - readOnly: true, - onTap: () async { - final dynamic datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalScreen( - profession: - _serviceTypeController.text, - ); - }, - ), - ); - if (datos != null) { - Professional? profesional = datos[0]; - ubicacion = datos[1]; - - if (profesional != null) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - - professionalId = profesional.id; - print(profesional); - if (profesional.token != '') { - professionalToken = - profesional.token!; - print(professionalToken); - } - - professionalTarifa = - profesional.tarifa ?? 0; - - professionalUbicacion = - profesional.ubicacion; - - professionalLatitude = - profesional.latitude; - professionalLongitude = - profesional.longitude; - - if (ubicacion == 'sitio') { - professionalAddress = - profesional.realAddress; - - _locationController.text = - profesional.realAddress; - - if (professionalLatitude != null && - professionalLatitude != 0 && - professionalLongitude != null && - professionalLongitude != 0) { - googleMapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: LatLng( - professionalLatitude!, - professionalLongitude!, - ), - zoom: 17), - ), - ); - } - } - }); - } - } - }, - decoration: const InputDecoration( - prefixIcon: - Icon(Icons.assignment_ind_rounded), - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Seleccionar profesional'), - ), - ], - ), - const SizedBox(height: 15), - TextFormField( - controller: _locationController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.near_me), - hintText: 'Dirección', - ), - onChanged: (value) { - String modifiedValue = - value.replaceAll(' ', '_'); - placeAutoComplete(modifiedValue); - }, - ), - Container( - height: _placesList.isNotEmpty - ? 200 - : 0, // Ajusta la altura según tus necesidades - child: ListView.builder( - itemCount: _placesList.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - final selectedAddress = _placesList[index] - ['formatted_address']; - final selectedLatitude = - _placesList[index]['geometry'] - ['location']['lat']; - final selectedLongitude = - _placesList[index]['geometry'] - ['location']['lng']; - - _locationController.text = - selectedAddress; - - final newCoordinates = LatLng( - selectedLatitude, selectedLongitude); - googleMapController?.animateCamera( - CameraUpdate.newLatLng(newCoordinates), - ); - - setState(() { - _placesList = []; - }); - }, - title: Text(_placesList[index] - ['formatted_address']), - ); - }, - ), - ), - const SizedBox(height: 15), - Row( - children: [ - SizedBox( - width: 180, - child: TextFormField( - onTap: () { - if (_profesionalController - .text.isNotEmpty) { - _selectDate(context); - } else { - final snackBar = SnackBar( - backgroundColor: Colors.blue, - content: const Text( - 'Selecciona un profesional', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - elevation: 8.0, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10.0), - ), - ); - - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - readOnly: true, - decoration: InputDecoration( - prefixIcon: - const Icon(Icons.calendar_month), - suffixIcon: _selectedDate == null - ? const Icon(Icons.arrow_drop_down) - : null, - hintText: 'Fecha', - ), - controller: TextEditingController( - text: _selectedDate == null - ? '' - : formatter.format(_selectedDate!)), - ), - ), - SizedBox( - width: 140, - child: TextFormField( - onTap: () { - if (_profesionalController - .text.isNotEmpty) { - _selectTime(context); - } else { - final snackBar = SnackBar( - backgroundColor: Colors.blue, - content: const Text( - 'Selecciona un profesional', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - elevation: 8.0, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10.0), - ), - ); - - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - readOnly: true, - decoration: const InputDecoration( - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Hora', - ), - controller: TextEditingController( - text: _selectedTime == null - ? '' - : ' ${_selectedTime!.format(context)}'), - ), - ), - ], - ), - const SizedBox(height: 15), - TextFormField( - controller: _observacionController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.message_outlined), - hintText: 'Observaciones'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - if (user?.name == null || - user?.name == '' || - user?.phoneNumber == null || - user?.phoneNumber == '') { - Navigator.push( + ), + ], + ), + Expanded( + child: SingleChildScrollView( + child: Container( + width: double.infinity, + height: MediaQuery.of(context).size.height * 0.5, + decoration: const BoxDecoration(color: Colors.white), + child: Column( + children: [ + SizedBox( + width: 320, + child: Form( + child: Column( + children: [ + TextFormField( + controller: _serviceTypeController, + readOnly: true, + onTap: () async { + final String? serviceType = + await Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { - return const ProfileScreen(); + return const ServiceTypeScreen(); }, ), - ); - } else { - try { - final DateTime combinedDate = DateTime( - _selectedDate!.year, - _selectedDate!.month, - _selectedDate!.day, - _selectedTime!.hour, - _selectedTime!.minute); + ) as String?; - DateTime time2 = combinedDate - .add(const Duration(hours: 2)); - - UserModel.getUser(uid.toString()) - .then((value) { - eventoService - .createEvent( - value.name, - _observacionController.text, - '$_selectedDate', - '$combinedDate', - '$time2', - professionalId, - ubicacion, - _locationController.text, - ubicacion != 'sitio' - ? coordinates.latitude - : professionalLatitude, - ubicacion != 'sitio' - ? coordinates.longitude - : professionalLongitude, - 'pendiente', - professionalTarifa == 0 - ? 0 - : professionalTarifa, - false, - false, - ) - .then((value) { - if (professionalToken != '') { - sendPushNotification( - professionalToken); - } - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ServiceAfterScreen( - eventoId: value, - ); - }, - ), - ); - }); + if (serviceType != null) { + setState(() { + _serviceType = serviceType; + _serviceTypeController.text = + _serviceType; }); - } catch (e) { - Get.snackbar( - 'Llena todos los campos', - 'Asegurate de llenar todos los campos.', - snackPosition: SnackPosition.BOTTOM, - ); } - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 50), + }, + decoration: const InputDecoration( + prefixIcon: + Icon(Icons.room_service_rounded), + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Tipo de Servicio'), ), - child: const Text( - 'Solicitar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, + const SizedBox(height: 15), + Column( + children: [ + TextFormField( + controller: _profesionalController, + readOnly: true, + onTap: () async { + final dynamic datos = + await Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ProfessionalScreen( + profession: + _serviceTypeController.text, + ); + }, + ), + ); + if (datos != null) { + Professional? profesional = datos[0]; + ubicacion = datos[1]; + + if (profesional != null) { + setState(() { + _profesionalController.text = + profesional.name.toString(); + + professionalId = profesional.id; + + if (profesional.token != '') { + professionalToken = + profesional.token!; + } + + professionalTarifa = + profesional.tarifa ?? 0; + + professionalUbicacion = + profesional.ubicacion; + + professionalLatitude = + profesional.latitude; + professionalLongitude = + profesional.longitude; + + if (ubicacion == 'sitio') { + professionalAddress = + profesional.realAddress; + + _locationController.text = + profesional.realAddress; + + if (professionalLatitude != + null && + professionalLatitude != 0 && + professionalLongitude != + null && + professionalLongitude != 0) { + googleMapController + .animateCamera( + CameraUpdate + .newCameraPosition( + CameraPosition( + target: LatLng( + professionalLatitude!, + professionalLongitude!, + ), + zoom: 17), + ), + ); + } + } + }); + } + } + }, + decoration: const InputDecoration( + prefixIcon: + Icon(Icons.assignment_ind_rounded), + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Seleccionar profesional'), + ), + ], + ), + const SizedBox(height: 15), + TextFormField( + controller: _locationController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.near_me), + hintText: 'Dirección', + ), + onChanged: (value) { + String modifiedValue = + value.replaceAll(' ', '_'); + placeAutoComplete(modifiedValue); + }, + ), + SizedBox( + height: _placesList.isNotEmpty + ? 200 + : 0, // Ajusta la altura según tus necesidades + child: ListView.builder( + itemCount: _placesList.length, + itemBuilder: (context, index) { + return ListTile( + onTap: () { + final selectedAddress = + _placesList[index] + ['formatted_address']; + final selectedLatitude = + _placesList[index]['geometry'] + ['location']['lat']; + final selectedLongitude = + _placesList[index]['geometry'] + ['location']['lng']; + + _locationController.text = + selectedAddress; + + final newCoordinates = LatLng( + selectedLatitude, + selectedLongitude); + googleMapController.animateCamera( + CameraUpdate.newLatLng( + newCoordinates), + ); + + setState(() { + _placesList = []; + }); + }, + title: Text(_placesList[index] + ['formatted_address']), + ); + }, ), ), - ), - //Text('${_selectedDate?.weekday}'), - ], + const SizedBox(height: 15), + Row( + children: [ + SizedBox( + width: 180, + child: TextFormField( + onTap: () { + if (_profesionalController + .text.isNotEmpty) { + _selectDate(context); + } else { + final snackBar = SnackBar( + backgroundColor: Colors.blue, + content: const Text( + 'Selecciona un profesional', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + elevation: 8.0, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10.0), + ), + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + readOnly: true, + decoration: InputDecoration( + prefixIcon: + const Icon(Icons.calendar_month), + suffixIcon: _selectedDate == null + ? const Icon(Icons.arrow_drop_down) + : null, + hintText: 'Fecha', + ), + controller: TextEditingController( + text: _selectedDate == null + ? '' + : formatter + .format(_selectedDate!)), + ), + ), + SizedBox( + width: 140, + child: TextFormField( + onTap: () { + if (_profesionalController + .text.isNotEmpty) { + _selectTime(context); + } else { + final snackBar = SnackBar( + backgroundColor: Colors.blue, + content: const Text( + 'Selecciona un profesional', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + elevation: 8.0, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10.0), + ), + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + readOnly: true, + decoration: const InputDecoration( + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}'), + ), + ), + ], + ), + const SizedBox(height: 15), + TextFormField( + controller: _observacionController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.message_outlined), + hintText: 'Observaciones'), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + if (user?.name == null || + user?.name == '' || + user?.phoneNumber == null || + user?.phoneNumber == '') { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ProfileScreen(); + }, + ), + ); + } else { + try { + final DateTime combinedDate = DateTime( + _selectedDate!.year, + _selectedDate!.month, + _selectedDate!.day, + _selectedTime!.hour, + _selectedTime!.minute); + + DateTime time2 = combinedDate + .add(const Duration(hours: 2)); + + UserModel.getUser(uid.toString()) + .then((value) { + eventoService + .createEvent( + value.name, + _observacionController.text, + '$_selectedDate', + '$combinedDate', + '$time2', + professionalId, + ubicacion, + _locationController.text, + ubicacion != 'sitio' + ? coordinates.latitude + : professionalLatitude, + ubicacion != 'sitio' + ? coordinates.longitude + : professionalLongitude, + 'pendiente', + professionalTarifa == 0 + ? 0 + : professionalTarifa, + false, + false, + ) + .then((value) { + if (professionalToken != '') { + sendPushNotification( + professionalToken); + } + Navigator.pushReplacement( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ServiceAfterScreen( + eventoId: value, + ); + }, + ), + ); + }); + }); + } catch (e) { + Get.snackbar( + 'Llena todos los campos', + 'Asegurate de llenar todos los campos.', + snackPosition: SnackPosition.BOTTOM, + ); + } + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2BA4EC), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + elevation: 0, + minimumSize: const Size(230, 50), + ), + child: const Text( + 'Solicitar servicio', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ), + ], + ), ), ), - ), - ], + ], + ), ), ), - ], - ), + ), + ], ), ), ); diff --git a/lib/src/screens/solicitudes.dart b/lib/src/screens/solicitudes.dart index 0aead74..385ae99 100644 --- a/lib/src/screens/solicitudes.dart +++ b/lib/src/screens/solicitudes.dart @@ -75,6 +75,7 @@ class SolicitudScreen extends StatelessWidget { try { eventos.addAll(snapshot.data!); + eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1)); } catch (e) { print("Error" + e.toString()); } diff --git a/lib/src/screens/support.dart b/lib/src/screens/support.dart index cadd749..a2a8572 100644 --- a/lib/src/screens/support.dart +++ b/lib/src/screens/support.dart @@ -19,9 +19,7 @@ class SsupportStateScreen extends State { final String body = ''; Future _sendWhatsapp(String? number) async { - final _whatsappUrl = - 'https://wa.me/$number?text=${Uri.parse('Hola! soy usuario de Prosapp y quisiera conocer mas sobre esta app')}'; - + final _whatsappUrl = 'https://api.whatsapp.com/send?phone=$number&text=Hola%21+soy+usuario+de+Prosapp+y+quisiera+conocer+mas+sobre+esta+app+%F0%9F%98%81'; if (!await launch(_whatsappUrl)) { throw Exception('Could not launch $_whatsappUrl'); } @@ -39,14 +37,10 @@ class SsupportStateScreen extends State { } void _sendEmailWeb(String recipients) async { - final email = - 'mailto:$recipients?subject=${Uri.encodeComponent(recipients)}&body=${Uri.encodeComponent(body)}'; + final email = 'mailto:$recipients?subject=${Uri.encodeComponent(recipients)}&body=${Uri.encodeComponent(body)}'; if (await canLaunch(email)) { await launch(email); - } else { - // No se pudo abrir el cliente de correo electrónico - // Puedes mostrar un diálogo o realizar otra acción en este caso - } + } else {} } @override diff --git a/lib/src/services/firebase_messaging.dart b/lib/src/services/firebase_messaging.dart index a590b62..fe6d259 100644 --- a/lib/src/services/firebase_messaging.dart +++ b/lib/src/services/firebase_messaging.dart @@ -1,4 +1,6 @@ import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:get/get.dart'; + class FirebaseMessagingService { FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; @@ -18,11 +20,22 @@ class FirebaseMessagingService { String? token = await _firebaseMessaging.getToken(); print('Token FCM: $token'); - // Escuchar los mensajes entrantes en la aplicación FirebaseMessaging.onMessage.listen((RemoteMessage message) { - print('Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}'); - // Aquí puedes mostrar la notificación o procesar los datos recibidos según tus necesidades - }); + print('Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}'); + + // Obtener el valor de la clave "screen" de los datos de la notificación + String? notificationScreen = message.data['screen']; + + // Navegar a la pantalla correspondiente según el valor de la clave "screen" + if (notificationScreen == "misservicios") { + // Navegar a MyServicesScreen + Get.offNamed('/misservicios'); + } else if (notificationScreen == "solicitud") { + // Navegar a SolicitudScreen + Get.offNamed('/solicitud'); + } +}); + // Manejar la notificación cuando se toca y la aplicación está en primer plano (opcional) FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b883437..3584be3 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -16,6 +16,7 @@ import firebase_storage import flutter_local_notifications import geolocator_apple import path_provider_foundation +import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -30,5 +31,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d6bfbce..89569c5 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + DFAE116AA0A5B93B39BF14BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,7 +55,7 @@ /* Begin PBXFileReference section */ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* prosappco.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "prosappco.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* prosappco.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = prosappco.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -68,6 +69,7 @@ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -99,6 +101,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */, ); sourceTree = ""; }; @@ -227,6 +230,7 @@ files = ( 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + DFAE116AA0A5B93B39BF14BF /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/firebase_app_id_file.json b/macos/firebase_app_id_file.json index a4bc04d..1c89e48 100644 --- a/macos/firebase_app_id_file.json +++ b/macos/firebase_app_id_file.json @@ -1,7 +1,7 @@ { "file_generated_by": "FlutterFire CLI", "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", - "GOOGLE_APP_ID": "1:245204384533:ios:aeb233e55c5ddf7a9f871c", + "GOOGLE_APP_ID": "1:245204384533:ios:1d58f5624d0df4859f871c", "FIREBASE_PROJECT_ID": "prosapp-5747a", "GCM_SENDER_ID": "245204384533" } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 8a48901..be977ab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -960,6 +960,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: d29753996d8eb8f7619a1f13df6ce65e34bc107bef6330739ed76f18b22310ef + url: "https://pub.dev" + source: hosted + version: "2.3.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d + url: "https://pub.dev" + source: hosted + version: "2.3.0" simple_gesture_detector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4ff35aa..1c428ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: prosappco -description: A new Flutter project. +description: Encuentra personal médico certificado. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 +version: 1.1.0+1 environment: sdk: '>=2.19.3 <3.0.0' @@ -30,6 +30,7 @@ environment: dependencies: flutter: sdk: flutter + shared_preferences: ^2.0.10 # The following adds the Cupertino Icons font to your application. diff --git a/web/index.html b/web/index.html index 1aa37d6..8700331 100644 --- a/web/index.html +++ b/web/index.html @@ -23,7 +23,9 @@ - + + +