From 40ee4f2ab2986a80ad4e4f90a9fca2274b27bd38 Mon Sep 17 00:00:00 2001
From: Juan Felipe Duarte <70235683+DuarteJFelipe@users.noreply.github.com>
Date: Wed, 10 May 2023 08:43:40 -0500
Subject: [PATCH] fix
---
android/app/build.gradle | 1 +
android/app/src/main/AndroidManifest.xml | 9 +
lib/main.dart | 4 +-
lib/src/components/drawer_menu.dart | 38 +++-
lib/src/components/drawer_professional.dart | 26 ++-
lib/src/models/event_model.dart | 7 +-
lib/src/models/scores_model.dart | 6 +-
lib/src/models/setting_model.dart | 47 ++++-
lib/src/models/user_model.dart | 22 +--
lib/src/screens/about.dart | 83 +++++++++
lib/src/screens/calendar.dart | 52 ++++--
lib/src/screens/chat.dart | 8 +-
lib/src/screens/cita.dart | 157 +++++++++++++++-
lib/src/screens/configuracion.dart | 36 ++++
lib/src/screens/my_services.dart | 65 +++++--
lib/src/screens/my_services_pro.dart | 80 ++++++---
lib/src/screens/professional.dart | 26 +--
lib/src/screens/professional_info.dart | 2 +-
lib/src/screens/reputacion_pro.dart | 107 +++++++++++
lib/src/screens/reputation.dart | 5 +-
lib/src/screens/score.dart | 168 ++++++++++++++++++
lib/src/screens/service.dart | 56 +++---
lib/src/screens/solicitudes.dart | 34 +++-
lib/src/screens/support.dart | 145 +++++++++++++++
lib/src/screens/web_view.dart | 47 +++++
macos/Flutter/GeneratedPluginRegistrant.swift | 2 +
pubspec.lock | 80 ++++++++-
pubspec.yaml | 5 +-
.../flutter/generated_plugin_registrant.cc | 3 +
windows/flutter/generated_plugins.cmake | 1 +
30 files changed, 1175 insertions(+), 147 deletions(-)
create mode 100644 lib/src/screens/about.dart
create mode 100644 lib/src/screens/configuracion.dart
create mode 100644 lib/src/screens/reputacion_pro.dart
create mode 100644 lib/src/screens/score.dart
create mode 100644 lib/src/screens/support.dart
create mode 100644 lib/src/screens/web_view.dart
diff --git a/android/app/build.gradle b/android/app/build.gradle
index f900c60..f7f642f 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -70,4 +70,5 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation platform('com.google.firebase:firebase-bom:31.3.0')
+ implementation 'com.google.firebase:firebase-analytics-ktx'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index cc84a01..9187f5c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -2,6 +2,12 @@
package="com.example.prosappco">
+
+
+
+
+
+
+
+
+
diff --git a/lib/main.dart b/lib/main.dart
index 0db13c9..45af13b 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -19,6 +20,7 @@ import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/register.dart';
import 'package:prosappco/src/screens/request_sent.dart';
+import 'package:prosappco/src/screens/score.dart';
import 'package:prosappco/src/screens/service.dart';
import 'package:prosappco/src/screens/solicitudes.dart';
import 'firebase_options.dart';
@@ -64,7 +66,7 @@ class MyApp extends StatelessWidget {
'/profilePro': (context) => const ProfilePro(),
'/calendar': (context) => const CalendarScreen(),
'/solicitud': (context) => SolicitudScreen(),
- '/misserviciospro': (context) => MyServicesProScreen(),
+ '/misserviciospro': (context) => const MyServicesProScreen(),
'/misservicios': (context) => MyServicesScreen(),
},
onGenerateRoute: (settings) {
diff --git a/lib/src/components/drawer_menu.dart b/lib/src/components/drawer_menu.dart
index 1ac0f8a..1addff2 100644
--- a/lib/src/components/drawer_menu.dart
+++ b/lib/src/components/drawer_menu.dart
@@ -6,8 +6,11 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
+import 'package:prosappco/src/screens/configuracion.dart';
import 'package:prosappco/src/screens/messages_user.dart';
import 'package:prosappco/src/screens/profile.dart';
+import 'package:prosappco/src/screens/reputation.dart';
+import 'package:prosappco/src/screens/support.dart';
class DrawerMenu extends StatefulWidget {
@override
@@ -144,7 +147,16 @@ class _DrawerMenuState extends State {
),
),
ListTile(
- onTap: () {},
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const ConfiguracionScreen();
+ },
+ ),
+ );
+ },
leading: const Icon(
Icons.construction_outlined,
color: Colors.black,
@@ -155,7 +167,16 @@ class _DrawerMenuState extends State {
),
),
ListTile(
- onTap: () {},
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const SupportScreen();
+ },
+ ),
+ );
+ },
leading: const Icon(
Icons.question_mark_rounded,
color: Colors.black,
@@ -227,7 +248,16 @@ class _DrawerMenuState extends State {
color: const Color(0xFFD6F4FF),
child: ListTile(
tileColor: const Color(0xFFD6F4FF),
- onTap: () {},
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const ReputationScreen();
+ },
+ ),
+ );
+ },
trailing: const Icon(Icons.keyboard_arrow_right,
color: Colors.black),
title: const Text(
@@ -255,7 +285,7 @@ class _DrawerMenuState extends State {
),
const SizedBox(width: 5),
Text(
- '(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
+ '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
diff --git a/lib/src/components/drawer_professional.dart b/lib/src/components/drawer_professional.dart
index d8a119c..9d48560 100644
--- a/lib/src/components/drawer_professional.dart
+++ b/lib/src/components/drawer_professional.dart
@@ -14,6 +14,8 @@ import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/reputation.dart';
import '../models/scores_model.dart';
+import '../screens/configuracion.dart';
+import '../screens/support.dart';
class DrawerProfessional extends StatefulWidget {
@override
@@ -153,7 +155,16 @@ class _DrawerProfessionalState extends State {
),
),
ListTile(
- onTap: () {},
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const ConfiguracionScreen();
+ },
+ ),
+ );
+ },
leading: const Icon(
Icons.construction_outlined,
color: Colors.black,
@@ -164,7 +175,16 @@ class _DrawerProfessionalState extends State {
),
),
ListTile(
- onTap: () {},
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const SupportScreen();
+ },
+ ),
+ );
+ },
leading: const Icon(
Icons.question_mark_rounded,
color: Colors.black,
@@ -296,7 +316,7 @@ class _DrawerProfessionalState extends State {
),
const SizedBox(width: 5),
Text(
- '(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
+ '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
diff --git a/lib/src/models/event_model.dart b/lib/src/models/event_model.dart
index e51a1a1..f31a9a3 100644
--- a/lib/src/models/event_model.dart
+++ b/lib/src/models/event_model.dart
@@ -17,6 +17,7 @@ class EventoService {
double latitude,
double longitude,
String status,
+ int? tarifa,
) async {
try {
DateTime ahora = DateTime.now();
@@ -35,7 +36,8 @@ class EventoService {
'latitude': latitude,
'longitude': longitude,
'status': status,
- 'Timestamp': ahora
+ 'Timestamp': ahora,
+ 'tarifa': tarifa ?? 0,
}).then((value) {
FirebaseFirestore.instance.collection('users').doc(uid).update({
'services': FieldValue.arrayUnion([value.id])
@@ -135,6 +137,7 @@ class Event {
double? latitud;
String status;
ScoresModel? scoresModel;
+ int? tarifa;
Event({
this.id,
@@ -150,6 +153,7 @@ class Event {
this.longitud,
this.latitud,
this.status = 'pendiente',
+ this.tarifa,
});
factory Event.fromJson(Map json) {
@@ -167,6 +171,7 @@ class Event {
longitud: json['longitude'] ?? 0,
latitud: json['latitude'] ?? 0,
status: json['status'],
+ tarifa: json['tarifa'] ?? 0,
);
}
diff --git a/lib/src/models/scores_model.dart b/lib/src/models/scores_model.dart
index f7b8c99..3c685a1 100644
--- a/lib/src/models/scores_model.dart
+++ b/lib/src/models/scores_model.dart
@@ -30,7 +30,7 @@ class ScoresModel {
String userReceived, bool isFromClient, bool userInfo) async {
final receivedScoresQuery = FirebaseFirestore.instance
.collection('scores')
- .where('is_from_client', isEqualTo: isFromClient)
+ .where('is_from_professional', isEqualTo: isFromClient)
.where('to_user', isEqualTo: userReceived);
final receivedScoresSnapshot = await receivedScoresQuery.get();
@@ -48,7 +48,7 @@ class ScoresModel {
String userGiven, bool isFromClient, bool userInfo) async {
final givenScoresQuery = FirebaseFirestore.instance
.collection('scores')
- .where('is_from_client', isEqualTo: isFromClient)
+ .where('is_from_professional', isEqualTo: isFromClient)
.where('from_user', isEqualTo: userGiven);
final givenScoresSnapshot = await givenScoresQuery.get();
@@ -101,7 +101,7 @@ class ScoreDetailModel {
fromUser: data['from_user'],
toUser: data['to_user'],
comment: data['comment'],
- isFromClient: data['is_from_client'],
+ isFromClient: data['is_from_professional'],
name: user?.name ?? "...",
avatar: user?.photo);
} catch (e) {
diff --git a/lib/src/models/setting_model.dart b/lib/src/models/setting_model.dart
index 823f843..f3e743f 100644
--- a/lib/src/models/setting_model.dart
+++ b/lib/src/models/setting_model.dart
@@ -3,17 +3,51 @@ import 'package:cloud_firestore/cloud_firestore.dart';
class SettingModel {
final bool domicilios;
final bool tarifas;
+ final String titulo;
+ final String parrafo;
+ final String numero;
+ final String email;
+ final String dias;
+ final String horas;
+ final String version;
+ final String proliticasPrivacidad;
+ final String terminosCondiciones;
- SettingModel(this.domicilios, this.tarifas);
+ SettingModel(
+ this.domicilios,
+ this.tarifas,
+ this.titulo,
+ this.parrafo,
+ this.numero,
+ this.email,
+ this.dias,
+ this.horas,
+ this.version,
+ this.proliticasPrivacidad,
+ this.terminosCondiciones,
+ );
static Future fromJson(Map json) async {
try {
- if (json == null) return SettingModel(false, false);
+ if (json == null)
+ return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
- return SettingModel(json['domicilio'], json['tarifa']);
+ return SettingModel(
+ json['domicilio'],
+ json['tarifa'],
+ json['titulo_soporte'],
+ json['parrafo_soporte'],
+ json['numero_soporte'],
+ json['email_soporte'],
+ json['dias_soporte'],
+ json['horas_soporte'],
+ json['version'],
+ json['politicas_privacidad'],
+ json['terminos_condiciones'],
+ );
} catch (e) {
print('Error settings: $e');
- return SettingModel(false, false);
+ return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
}
}
@@ -24,11 +58,14 @@ class SettingModel {
.collection('settings')
.doc('global')
.get();
+
final Map? data = snapshot.data();
+
+ print('data $data');
return fromJson(data!);
} catch (e) {
print('Error getting settings: $e');
- return SettingModel(false, false);
+ return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
}
}
}
diff --git a/lib/src/models/user_model.dart b/lib/src/models/user_model.dart
index 3c4d750..b02aa02 100644
--- a/lib/src/models/user_model.dart
+++ b/lib/src/models/user_model.dart
@@ -8,25 +8,27 @@ class UserModel {
final String? profession;
final String? state;
final Reference? photo;
+ final int? tarifa;
- UserModel(this.name, this.city, this.profession, this.state, this.photo);
+ UserModel(this.name, this.city, this.profession, this.state, this.photo,
+ this.tarifa);
static Future fromJson(
Map? json, String uid) async {
try {
- if (json == null) return UserModel('', '', '', null, null);
+ if (json == null) return UserModel('', '', '', null, null, 0);
String? avatar = json['photo'];
return UserModel(
- json['name'],
- json['city'],
- json['profesion'],
- json['estado'],
- avatar != null ? storage.ref().child(avatar) : null,
- );
+ json['name'],
+ json['city'],
+ json['profesion'],
+ json['estado'],
+ avatar != null ? storage.ref().child(avatar) : null,
+ json['tarifa'] ?? 0);
} catch (e) {
print('XD user $e');
- return UserModel('', '', '', null, null);
+ return UserModel('', '', '', null, null, 0);
}
}
@@ -38,7 +40,7 @@ class UserModel {
return fromJson(data, uid);
} catch (e) {
print('Error getting user: $e');
- return UserModel('', '', '', null, null);
+ return UserModel('', '', '', null, null, 0);
}
}
}
diff --git a/lib/src/screens/about.dart b/lib/src/screens/about.dart
new file mode 100644
index 0000000..4fbb094
--- /dev/null
+++ b/lib/src/screens/about.dart
@@ -0,0 +1,83 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:prosappco/src/models/setting_model.dart';
+import 'package:prosappco/src/screens/web_view.dart';
+
+class AboutScreen extends StatefulWidget {
+ const AboutScreen({super.key});
+
+ @override
+ State createState() => _AboutScreenState();
+}
+
+class _AboutScreenState extends State {
+ SettingModel? settings;
+
+ @override
+ void initState() {
+ super.initState();
+ if (settings == null) {
+ SettingModel.getSettings().then(
+ (SettingModel value) => setState(() {
+ settings = value;
+ }),
+ );
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: PopAppbar(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ label: 'Acerca de la aplicación'),
+ body: ListView(
+ children: [
+ ListTile(
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return WebViewScreen(
+ label: 'Políticas de privacidad',
+ link: settings?.proliticasPrivacidad ?? '',
+ );
+ },
+ ),
+ );
+ },
+ title: const Text('Políticas de privacidad'),
+ trailing:
+ const Icon(Icons.keyboard_arrow_right, color: Colors.black),
+ ),
+ ListTile(
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return WebViewScreen(
+ label: 'Términos y condiciones',
+ link: settings?.terminosCondiciones ?? '',
+ );
+ },
+ ),
+ );
+ },
+ title: const Text('Términos y condiciones'),
+ trailing:
+ const Icon(Icons.keyboard_arrow_right, color: Colors.black),
+ ),
+ ListTile(
+ title: const Text('Versión de la aplicación'),
+ subtitle: Text(settings?.version ?? ''),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/src/screens/calendar.dart b/lib/src/screens/calendar.dart
index 38b480e..efb525e 100644
--- a/lib/src/screens/calendar.dart
+++ b/lib/src/screens/calendar.dart
@@ -5,7 +5,6 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/models/event_model.dart';
-import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:intl/intl.dart';
@@ -26,7 +25,10 @@ class _CalendarScreenState extends State {
EventoService eventoService = EventoService();
CalendarFormat _calendarFormat = CalendarFormat.month;
+
DateTime today = DateTime.now();
+ DateTime now = DateTime.now();
+
TimeOfDay? _selectedTime1;
TimeOfDay? _selectedTime2;
@@ -283,19 +285,36 @@ class _CalendarScreenState extends State {
),
PrimaryButtom(
onPressed: () async {
+ final DateTime combinedDate1 = DateTime(
+ today.year,
+ today.month,
+ today.day,
+ _selectedTime1!.hour,
+ _selectedTime1!.minute,
+ );
+ final DateTime combinedDate2 = DateTime(
+ today.year,
+ today.month,
+ today.day,
+ _selectedTime2!.hour,
+ _selectedTime2!.minute,
+ );
+
await eventoService
.createEvent(
- _titleController.text,
- _descriptionController.text,
- today.toString(),
- _selectedTime1!.format(context).toString(),
- _selectedTime2!.format(context).toString(),
- uid.toString(),
- '',
- '',
- 0,
- 0,
- 'aprobado')
+ _titleController.text,
+ _descriptionController.text,
+ DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(today),
+ '$combinedDate1',
+ '$combinedDate2',
+ uid.toString(),
+ '',
+ '',
+ 0,
+ 0,
+ 'aprobado',
+ 0,
+ )
.then((value) {
Navigator.pop(context);
_titleController.text = '';
@@ -323,8 +342,7 @@ class _CalendarScreenState extends State {
Widget _eventList() {
return FutureBuilder(
- future:
- getByProId('${DateFormat("yyyy-MM-dd 00:00:00.000").format(today)}Z'),
+ 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');
@@ -355,7 +373,9 @@ class _CalendarScreenState extends State {
),
);
},
- leading: Text(e.range1Hour1),
+ leading: Text(
+ TimeOfDay.fromDateTime(DateTime.parse(e.range1Hour1))
+ .format(context)),
title: RichText(
text: TextSpan(
children: [
@@ -402,7 +422,7 @@ class _CalendarScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toString()}'),
+ '(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
diff --git a/lib/src/screens/chat.dart b/lib/src/screens/chat.dart
index f777799..4ec96d9 100644
--- a/lib/src/screens/chat.dart
+++ b/lib/src/screens/chat.dart
@@ -37,12 +37,6 @@ class _ChatScreenState extends State {
}
}
});
-
- // if (scoresModel == null) {
- // ScoresModel.scoreTo(uid.toString(), false, false).then(
- // (ScoresModel s) => setState(() => scoresModel = s),
- // );
- // }
}
@override
@@ -62,7 +56,7 @@ class _ChatScreenState extends State {
color: Colors.grey.withOpacity(0.3),
spreadRadius: 2,
blurRadius: 3,
- offset: const Offset(0, 2), // changes position of shadow
+ offset: const Offset(0, 2),
),
],
),
diff --git a/lib/src/screens/cita.dart b/lib/src/screens/cita.dart
index 544653d..ad3fda7 100644
--- a/lib/src/screens/cita.dart
+++ b/lib/src/screens/cita.dart
@@ -3,6 +3,7 @@ import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
+import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
@@ -11,6 +12,7 @@ import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
import 'package:prosappco/src/screens/chat.dart';
+import 'package:prosappco/src/screens/score.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:community_material_icon/community_material_icon.dart';
@@ -25,13 +27,20 @@ class CitaScreen extends StatefulWidget {
class _CitaScreenState extends State {
final uid = AuthenticationRepository.instance.getCurrentUserUid();
UserModel? user;
- @override
+ DateTime today = DateTime.now();
String nombre = '';
+ int tarifa = 0;
Reference? ref_photo;
ScoresModel? scoresModel;
final phoneNumber = '+573022548060';
+ String formatCurrency(int number) {
+ final formatter =
+ NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: '');
+ return '\$${formatter.format(number)}';
+ }
+
Future _openMap(double lat, double lng) async {
final Uri _url =
Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng');
@@ -73,17 +82,25 @@ class _CitaScreenState extends State {
@override
Widget build(BuildContext context) {
+ today.difference(DateTime.parse(widget.evento.range1Hour1));
+ // DateTime todays = DateFormat('hh:mm a').parse(today.timeZoneName);
+ final eventDate = DateFormat('yyyy-MM-dd').parse(widget.evento.day);
+
if (nombre == '') {
if (uid != widget.evento.userId) {
UserModel.getUser(widget.evento.userId).then((value) {
- setState(() {
- nombre = value.name;
- ref_photo = value.photo;
+ UserModel.getUser(uid.toString()).then((me) {
+ setState(() {
+ tarifa = me.tarifa ?? 0;
+ nombre = value.name;
+ ref_photo = value.photo;
+ });
});
});
} else {
UserModel.getUser(widget.evento.professionalId).then((value) {
setState(() {
+ tarifa = value.tarifa ?? 0;
nombre = value.name;
ref_photo = value.photo;
});
@@ -122,7 +139,7 @@ class _CitaScreenState extends State {
),
TextSpan(
text:
- '${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${widget.evento.range1Hour1}',
+ '${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${TimeOfDay.fromDateTime(DateTime.parse(widget.evento.range1Hour1)).format(context)}',
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
@@ -151,7 +168,7 @@ class _CitaScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
+ '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
@@ -194,6 +211,18 @@ class _CitaScreenState extends State {
],
),
),
+ Column(
+ children: [
+ Text(
+ formatCurrency(tarifa),
+ style: const TextStyle(
+ fontWeight: FontWeight.w600, fontSize: 25),
+ ),
+ Text('Tarifa consulta ${widget.evento.tarifa}',
+ style: const TextStyle(fontSize: 15)),
+ ],
+ ),
+ const SizedBox(height: 15),
Text(
textAlign: TextAlign.center,
'" ${widget.evento.description} "',
@@ -367,6 +396,80 @@ class _CitaScreenState extends State {
],
),
),
+ widget.evento.status == 'aprobado'
+ ? eventDate.year == today.year &&
+ eventDate.month == today.month &&
+ eventDate.day == today.day
+ ? (DateTime.now()
+ .difference(
+ DateTime.parse(widget.evento.range1Hour1))
+ .abs() <=
+ const Duration(minutes: 30))
+ ? Padding(
+ padding: const EdgeInsets.only(bottom: 30),
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 20),
+ child: ElevatedButton(
+ onPressed: () {
+ FirebaseFirestore.instance
+ .collection("services")
+ .doc('${widget.evento.id}')
+ .update({"status": "iniciado"});
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFF2BA4EC),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ ),
+ elevation: 0,
+ minimumSize: const Size(230, 60),
+ ),
+ child: const Text(
+ 'Iniciar servicio',
+ style: TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ FirebaseFirestore.instance
+ .collection("services")
+ .doc('${widget.evento.id}')
+ .update({"status": "denegado"}).then(
+ (value) {
+ Navigator.pushReplacementNamed(
+ context, '/solicitud');
+ });
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFFEC2B2B),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ ),
+ elevation: 0,
+ minimumSize: const Size(230, 60),
+ ),
+ child: const Text(
+ 'Cancelar servicio',
+ style: TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ],
+ ),
+ )
+ : const SizedBox()
+ : const SizedBox()
+ : const SizedBox(),
widget.evento.status == 'pendiente'
? Padding(
padding: const EdgeInsets.only(bottom: 30),
@@ -433,6 +536,48 @@ class _CitaScreenState extends State {
),
)
: const SizedBox(),
+ widget.evento.status == 'iniciado'
+ ? Padding(
+ padding: const EdgeInsets.only(bottom: 30),
+ child: Column(
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ FirebaseFirestore.instance
+ .collection("services")
+ .doc('${widget.evento.id}')
+ .update({"status": "terminado"}).then((value) {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return ScoreScreen(evento: widget.evento);
+ },
+ ),
+ );
+ });
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFF2BA4EC),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ ),
+ elevation: 0,
+ minimumSize: const Size(230, 60),
+ ),
+ child: const Text(
+ 'Terminar servicio',
+ style: TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ],
+ ),
+ )
+ : const SizedBox(),
],
),
);
diff --git a/lib/src/screens/configuracion.dart b/lib/src/screens/configuracion.dart
new file mode 100644
index 0000000..a3e92f9
--- /dev/null
+++ b/lib/src/screens/configuracion.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:prosappco/src/screens/about.dart';
+
+class ConfiguracionScreen extends StatelessWidget {
+ const ConfiguracionScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: PopAppbar(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ label: 'Configuración'),
+ body: ListTile(
+ onTap: () {
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return const AboutScreen();
+ },
+ ),
+ );
+ },
+ title: const Text('Acerca de la aplicación'),
+ trailing: const Icon(
+ Icons.keyboard_arrow_right,
+ color: Colors.black,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/screens/my_services.dart b/lib/src/screens/my_services.dart
index 36954dc..f09a59b 100644
--- a/lib/src/screens/my_services.dart
+++ b/lib/src/screens/my_services.dart
@@ -1,10 +1,13 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
+import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
+import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class MyServicesScreen extends StatefulWidget {
@@ -16,6 +19,7 @@ class MyServicesScreen extends StatefulWidget {
class _MyServicesScreenState extends State {
DateTime today = DateTime.now();
+ final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
void initState() {
@@ -44,10 +48,32 @@ class _MyServicesScreenState extends State {
}
Widget _eventList() {
- return FutureBuilder(
- future: getByUserIdAll('aprobado', 'denegado'),
+ return StreamBuilder>(
+ stream: FirebaseFirestore.instance
+ .collection('services')
+ .where('user_id', isEqualTo: uid)
+ .where('status',
+ whereIn: ['aprobado', 'denegado', 'iniciado', 'terminado'])
+ .snapshots()
+ .asyncMap((snapshot) async {
+ try {
+ List eventos = [];
+ for (var element in snapshot.docs) {
+ final event = Event.fromJson(element.data());
+ event.scoresModel =
+ await ScoresModel.scoreTo(event.userId, false, false);
+ event.id = element.id;
+ eventos.add(event);
+ }
+ print('eventos $eventos');
+ return eventos;
+ } catch (e) {
+ print('Error getByProId $e');
+ return [];
+ }
+ }),
builder: (BuildContext context, AsyncSnapshot> snapshot) {
- if (!snapshot.hasData) {
+ if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
@@ -67,13 +93,14 @@ class _MyServicesScreenState extends State {
child: Center(child: Text('No tienes citas')),
);
}
+
return Column(
children: [
...eventos.map(
(event) => ListTile(
- tileColor: event.status == 'aprobado'
- ? Colors.blue[100]
- : Colors.red[100],
+ tileColor: event.status == 'denegado'
+ ? Colors.red[100]
+ : Colors.blue[100],
onTap: () {
Navigator.push(
context,
@@ -90,11 +117,23 @@ class _MyServicesScreenState extends State {
color: Colors.blue,
size: 40,
)
- : const Icon(
- Icons.close,
- color: Colors.red,
- 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: [
@@ -108,7 +147,7 @@ class _MyServicesScreenState extends State {
),
TextSpan(
text:
- '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}',
+ '${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,
@@ -141,7 +180,7 @@ class _MyServicesScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
+ '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
diff --git a/lib/src/screens/my_services_pro.dart b/lib/src/screens/my_services_pro.dart
index d23b821..0488ea9 100644
--- a/lib/src/screens/my_services_pro.dart
+++ b/lib/src/screens/my_services_pro.dart
@@ -1,13 +1,17 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
+import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
+import 'package:prosappco/src/models/scores_model.dart';
+import 'package:prosappco/src/screens/cita.dart';
class MyServicesProScreen extends StatefulWidget {
- MyServicesProScreen({super.key});
+ const MyServicesProScreen({super.key});
@override
State createState() => _MyServicesProScreenState();
@@ -15,6 +19,7 @@ class MyServicesProScreen extends StatefulWidget {
class _MyServicesProScreenState extends State {
DateTime today = DateTime.now();
+ final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
void initState() {
@@ -43,10 +48,32 @@ class _MyServicesProScreenState extends State {
}
Widget _eventList() {
- return FutureBuilder(
- future: getByProIdAll('aprobado', 'denegado'),
+ return StreamBuilder>(
+ stream: FirebaseFirestore.instance
+ .collection('services')
+ .where('professional_id', isEqualTo: uid)
+ .where('status',
+ whereIn: ['aprobado', 'denegado', 'iniciado', 'terminado'])
+ .snapshots()
+ .asyncMap((snapshot) async {
+ try {
+ List eventos = [];
+ for (var element in snapshot.docs) {
+ final event = Event.fromJson(element.data());
+ event.scoresModel =
+ await ScoresModel.scoreTo(event.userId, false, false);
+ event.id = element.id;
+ eventos.add(event);
+ }
+ print('eventos $eventos');
+ return eventos;
+ } catch (e) {
+ print('Error getByProId $e');
+ return [];
+ }
+ }),
builder: (BuildContext context, AsyncSnapshot> snapshot) {
- if (!snapshot.hasData) {
+ if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
@@ -66,24 +93,23 @@ class _MyServicesProScreenState extends State {
child: Center(child: Text('No tienes citas')),
);
}
+
return Column(
children: [
...eventos.map(
(event) => ListTile(
- tileColor: event.status == 'aprobado'
- ? Colors.blue[100]
- : Colors.red[100],
+ tileColor: event.status == 'denegado'
+ ? Colors.red[100]
+ : Colors.blue[100],
onTap: () {
- print(event.id);
-
- // Navigator.push(
- // context,
- // CupertinoPageRoute(
- // builder: (BuildContext context) {
- // return CitaScreen(evento: event);
- // },
- // ),
- // );
+ Navigator.push(
+ context,
+ CupertinoPageRoute(
+ builder: (BuildContext context) {
+ return CitaScreen(evento: event);
+ },
+ ),
+ );
},
leading: event.status == 'aprobado'
? const Icon(
@@ -91,11 +117,17 @@ class _MyServicesProScreenState extends State {
color: Colors.blue,
size: 40,
)
- : const Icon(
- Icons.close,
- color: Colors.red,
- size: 40,
- ),
+ : event.status == 'iniciado'
+ ? const Icon(
+ Icons.access_time,
+ color: Colors.blue,
+ size: 40,
+ )
+ : const Icon(
+ Icons.close,
+ color: Colors.red,
+ size: 40,
+ ),
title: RichText(
text: TextSpan(
children: [
@@ -109,7 +141,7 @@ class _MyServicesProScreenState extends State {
),
TextSpan(
text:
- '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}',
+ '${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,
@@ -142,7 +174,7 @@ class _MyServicesProScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
+ '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
diff --git a/lib/src/screens/professional.dart b/lib/src/screens/professional.dart
index cff9935..402d134 100644
--- a/lib/src/screens/professional.dart
+++ b/lib/src/screens/professional.dart
@@ -36,6 +36,7 @@ class Professional {
final double longitude;
final List professionalEspecializado;
final ScoresModel scores;
+ final int? tarifa;
Professional({
required this.id,
@@ -49,6 +50,7 @@ class Professional {
required this.realAddress,
required this.latitude,
required this.longitude,
+ this.tarifa,
});
String getEspecializaciones() {
@@ -90,18 +92,18 @@ class Professional {
.toList();
Professional professional = Professional(
- id: user.id,
- name: data['name'],
- professionName: data['profesion'],
- cityName: data['city'],
- professionalRef: storage.ref().child(photo),
- professionalEspecializado: especializaciones,
- ubicacion: data['ubicacion'] ?? '',
- realAddress: data['address'] ?? '',
- latitude: data['latitude'] ?? 0,
- longitude: data['longitude'] ?? 0,
- scores: await ScoresModel.scoreFrom(uid, true, true),
- );
+ id: user.id,
+ name: data['name'],
+ professionName: data['profesion'],
+ cityName: data['city'],
+ professionalRef: storage.ref().child(photo),
+ professionalEspecializado: especializaciones,
+ ubicacion: data['ubicacion'] ?? '',
+ realAddress: data['address'] ?? '',
+ latitude: data['latitude'] ?? 0,
+ longitude: data['longitude'] ?? 0,
+ scores: await ScoresModel.scoreFrom(uid, true, true),
+ tarifa: data['tarifa'] ?? 0);
return professional;
}
} catch (e) {
diff --git a/lib/src/screens/professional_info.dart b/lib/src/screens/professional_info.dart
index 835666b..abd78de 100644
--- a/lib/src/screens/professional_info.dart
+++ b/lib/src/screens/professional_info.dart
@@ -170,7 +170,7 @@ class _ProfessionalInfoScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${widget.professional.scores.total.toString()}) ${widget.professional.scores.average.toString()}'),
+ '(${widget.professional.scores.total.toString()}) ${widget.professional.scores.average.toStringAsFixed(1)}'),
],
),
),
diff --git a/lib/src/screens/reputacion_pro.dart b/lib/src/screens/reputacion_pro.dart
new file mode 100644
index 0000000..0f8b2f4
--- /dev/null
+++ b/lib/src/screens/reputacion_pro.dart
@@ -0,0 +1,107 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_rating_bar/flutter_rating_bar.dart';
+import 'package:prosappco/src/authentication/authentication_repository.dart';
+import 'package:prosappco/src/components/photo_view.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:prosappco/src/models/scores_model.dart';
+
+class ReputationProScreen extends StatefulWidget {
+ const ReputationProScreen({
+ super.key,
+ });
+
+ @override
+ State createState() => _ReputationProScreenState();
+}
+
+class _ReputationProScreenState extends State {
+ ScoresModel? scoresModel;
+
+ @override
+ void initState() {
+ super.initState();
+ final uid = AuthenticationRepository.instance.getCurrentUserUid();
+
+ if (scoresModel == null) {
+ ScoresModel.scoreFrom(uid.toString(), false, true).then(
+ (ScoresModel s) => setState(() => scoresModel = s),
+ );
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: PopAppbar(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ label: 'Reputación'),
+ body: SingleChildScrollView(
+ child: Column(
+ children: [
+ ..._scoresList(scoresModel?.details ?? []),
+ ],
+ )),
+ );
+ }
+
+ List _scoresList(List list) {
+ return list.map((e) => _scoreItem(e)).toList();
+ }
+
+ Widget _scoreItem(ScoreDetailModel scoreDetails) {
+ return ListTile(
+ onTap: () {},
+ leading: ReferencePhoto(
+ ref: scoreDetails.avatar,
+ size: 50,
+ sizeCircle: 50,
+ sizeIcon: 35,
+ ),
+ title: Row(
+ children: [
+ RatingBar.builder(
+ initialRating: scoreDetails.score,
+ minRating: 1,
+ direction: Axis.horizontal,
+ allowHalfRating: true,
+ itemCount: 5,
+ itemSize: 22,
+ maxRating: 5,
+ itemPadding: const EdgeInsets.symmetric(horizontal: 0),
+ itemBuilder: (context, _) => const Icon(
+ Icons.star,
+ color: Color(0xFF2BA4EC),
+ ),
+ onRatingUpdate: (rating) {},
+ ignoreGestures: true,
+ ),
+ Text(
+ ' (${scoreDetails.score})',
+ style: const TextStyle(color: Colors.black54, fontSize: 13),
+ )
+ ],
+ ),
+ subtitle: Row(
+ children: [
+ Expanded(
+ child: Text.rich(
+ TextSpan(
+ children: [
+ TextSpan(
+ text: '${scoreDetails.name}, ',
+ style: const TextStyle(fontSize: 15, color: Colors.black),
+ ),
+ TextSpan(
+ text: '"${scoreDetails.comment}"',
+ style: const TextStyle(fontSize: 15, color: Colors.grey),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ));
+ }
+}
diff --git a/lib/src/screens/reputation.dart b/lib/src/screens/reputation.dart
index 1aa327c..4b3fd7a 100644
--- a/lib/src/screens/reputation.dart
+++ b/lib/src/screens/reputation.dart
@@ -4,10 +4,9 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/scores_model.dart';
-import 'package:prosappco/src/models/user_model.dart';
class ReputationScreen extends StatefulWidget {
- ReputationScreen({
+ const ReputationScreen({
super.key,
});
@@ -24,7 +23,7 @@ class _ReputationScreenState extends State {
final uid = AuthenticationRepository.instance.getCurrentUserUid();
if (scoresModel == null) {
- ScoresModel.scoreTo(uid.toString(), true, true).then(
+ ScoresModel.scoreTo(uid.toString(), false, true).then(
(ScoresModel s) => setState(() => scoresModel = s),
);
}
diff --git a/lib/src/screens/score.dart b/lib/src/screens/score.dart
new file mode 100644
index 0000000..3bf14f4
--- /dev/null
+++ b/lib/src/screens/score.dart
@@ -0,0 +1,168 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_storage/firebase_storage.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_rating_bar/flutter_rating_bar.dart';
+import 'package:intl/intl.dart';
+import 'package:prosappco/src/components/photo_view.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:prosappco/src/models/event_model.dart';
+import 'package:prosappco/src/models/user_model.dart';
+
+class ScoreScreen extends StatefulWidget {
+ final Event evento;
+
+ const ScoreScreen({super.key, required this.evento});
+
+ @override
+ State createState() => ScoreScreenState();
+}
+
+class ScoreScreenState extends State {
+ TextEditingController commentController = TextEditingController();
+ Reference? ref_photo;
+ String nombre = '';
+ double _rating = 1.0;
+
+ @override
+ Widget build(BuildContext context) {
+ if (nombre == '') {
+ if (uid != widget.evento.userId) {
+ UserModel.getUser(widget.evento.userId).then((value) {
+ UserModel.getUser(uid.toString()).then((me) {
+ setState(() {
+ nombre = value.name;
+ ref_photo = value.photo;
+ });
+ });
+ });
+ } else {
+ UserModel.getUser(widget.evento.professionalId).then((value) {
+ setState(() {
+ nombre = value.name;
+ ref_photo = value.photo;
+ });
+ });
+ }
+ }
+
+ return Scaffold(
+ appBar: PopAppbar(onPressed: () {}, label: 'Puntuación'),
+ body: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 15),
+ child: ListTile(
+ leading: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5),
+ child: ReferencePhoto(
+ ref: ref_photo,
+ size: 50,
+ sizeCircle: 50,
+ sizeIcon: 35,
+ ),
+ ),
+ title: Text(
+ nombre,
+ style: const TextStyle(
+ color: Colors.black,
+ fontWeight: FontWeight.w600,
+ fontSize: 16),
+ ),
+ subtitle: Text(
+ '${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${TimeOfDay.fromDateTime(DateTime.parse(widget.evento.range1Hour1)).format(context)}'),
+ ),
+ ),
+ RatingBar.builder(
+ initialRating: _rating,
+ minRating: 1,
+ direction: Axis.horizontal,
+ allowHalfRating: true,
+ itemCount: 5,
+ itemSize: 40,
+ glow: false,
+ maxRating: 5,
+ itemPadding: const EdgeInsets.symmetric(horizontal: 5),
+ itemBuilder: (context, _) => const Icon(
+ Icons.star,
+ color: Color(0xFF2BA4EC),
+ ),
+ onRatingUpdate: (rating) {
+ setState(() {
+ _rating = rating;
+ });
+ },
+ ignoreGestures: false,
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 40),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ 'Comentario',
+ style: TextStyle(fontSize: 18),
+ ),
+ TextFormField(
+ maxLines: null,
+ keyboardType: TextInputType
+ .multiline, // establece el tipo de teclado como "multilinea"
+ controller: commentController,
+ ),
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 30),
+ child: Column(
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ if (widget.evento.professionalId == uid) {
+ FirebaseFirestore.instance.collection("scores").add({
+ "comment": commentController.text,
+ "from_user": uid,
+ "is_from_professional": false,
+ "score": _rating,
+ "to_user": widget.evento.userId,
+ }).then((value) {
+ // Navigator.pushReplacementNamed(context, '/score');
+ });
+ } else {
+ FirebaseFirestore.instance.collection("scores").add({
+ "comment": commentController.text,
+ "from_user": uid,
+ "is_from_professional": true,
+ "score": _rating,
+ "to_user": widget.evento.professionalId,
+ }).then((value) {
+ // Navigator.pushReplacementNamed(context, '/score');
+ });
+ }
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFF2BA4EC),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ ),
+ elevation: 0,
+ minimumSize: const Size(230, 60),
+ ),
+ child: const Text(
+ 'Enviar',
+ style: TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/src/screens/service.dart b/lib/src/screens/service.dart
index 64d3c04..5f6c290 100644
--- a/lib/src/screens/service.dart
+++ b/lib/src/screens/service.dart
@@ -1,5 +1,5 @@
import 'package:cloud_firestore/cloud_firestore.dart';
-import 'package:community_material_icon/community_material_icon.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@@ -35,6 +35,7 @@ class _ServiceScreenState extends State {
final _serviceTypeController = TextEditingController();
final _observacionController = TextEditingController();
String professionalId = '';
+ int professionalTarifa = 0;
String professionalAddress = '';
String professionalUbicacion = '';
double? professionalLatitude;
@@ -130,6 +131,7 @@ class _ServiceScreenState extends State {
final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid();
+ final fcmToken = FirebaseMessaging.instance.getToken();
BitmapDescriptor? _markerIcon;
@@ -411,6 +413,9 @@ class _ServiceScreenState extends State {
professionalId = profesional.id;
+ professionalTarifa =
+ profesional.tarifa ?? 0;
+
professionalUbicacion =
profesional.ubicacion;
@@ -549,35 +554,36 @@ class _ServiceScreenState extends State {
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
- int tiempo2 = ((_selectedTime!.hour * 3600 +
- _selectedTime!.minute * 60) +
- 7200) %
- 86400;
+ final DateTime combinedDate = DateTime(
+ _selectedDate!.year,
+ _selectedDate!.month,
+ _selectedDate!.day,
+ _selectedTime!.hour,
+ _selectedTime!.minute);
- int hora2 = tiempo2 ~/ 3600;
- int minutos2 = (tiempo2 % 3600) ~/ 60;
-
- TimeOfDay time2 =
- TimeOfDay(hour: hora2, minute: minutos2);
+ DateTime time2 =
+ combinedDate.add(const Duration(hours: 2));
UserModel.getUser(uid.toString()).then((value) {
eventoService
.createEvent(
- value.name,
- _observacionController.text,
- '${_selectedDate}Z',
- _selectedTime!.format(context),
- time2.format(context),
- professionalId,
- ubicacion,
- _locationController.text,
- ubicacion != 'sitio'
- ? coordinates.latitude
- : professionalLatitude,
- ubicacion != 'sitio'
- ? coordinates.longitude
- : professionalLongitude,
- 'pendiente')
+ value.name,
+ _observacionController.text,
+ '$_selectedDate',
+ '$combinedDate',
+ '$time2',
+ professionalId,
+ ubicacion,
+ _locationController.text,
+ ubicacion != 'sitio'
+ ? coordinates.latitude
+ : professionalLatitude,
+ ubicacion != 'sitio'
+ ? coordinates.longitude
+ : professionalLongitude,
+ 'pendiente',
+ professionalTarifa,
+ )
.then((value) {
Navigator.pushReplacement(
context,
diff --git a/lib/src/screens/solicitudes.dart b/lib/src/screens/solicitudes.dart
index dc5fab4..1e97e5c 100644
--- a/lib/src/screens/solicitudes.dart
+++ b/lib/src/screens/solicitudes.dart
@@ -1,9 +1,11 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/models/event_model.dart';
+import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class SolicitudScreen extends StatefulWidget {
@@ -50,8 +52,29 @@ class _SolicitudScreenState extends State {
}
Widget _eventList() {
- return FutureBuilder(
- future: getByProIdAll('pendiente', ''),
+ return StreamBuilder>(
+ stream: FirebaseFirestore.instance
+ .collection('services')
+ .where('professional_id', isEqualTo: uid)
+ .where('status', whereIn: ['pendiente', ''])
+ .snapshots()
+ .asyncMap((snapshot) async {
+ try {
+ List eventos = [];
+ for (var element in snapshot.docs) {
+ final event = Event.fromJson(element.data());
+ event.scoresModel =
+ await ScoresModel.scoreTo(event.userId, false, false);
+ event.id = element.id;
+ eventos.add(event);
+ }
+ print('eventos $eventos');
+ return eventos;
+ } catch (e) {
+ print('Error getByProId $e');
+ return [];
+ }
+ }),
builder: (BuildContext context, AsyncSnapshot> snapshot) {
if (!snapshot.hasData) {
return const Center(
@@ -87,7 +110,10 @@ class _SolicitudScreenState extends State {
),
);
},
- leading: Text(event.range1Hour1),
+ leading: Text(
+ TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1))
+ .format(context),
+ ),
title: RichText(
text: TextSpan(
children: [
@@ -134,7 +160,7 @@ class _SolicitudScreenState extends State {
),
const SizedBox(width: 5),
Text(
- '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
+ '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
diff --git a/lib/src/screens/support.dart b/lib/src/screens/support.dart
new file mode 100644
index 0000000..fd3922c
--- /dev/null
+++ b/lib/src/screens/support.dart
@@ -0,0 +1,145 @@
+import 'package:community_material_icon/community_material_icon.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_email_sender/flutter_email_sender.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:prosappco/src/models/setting_model.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class SupportScreen extends StatefulWidget {
+ const SupportScreen({super.key});
+
+ @override
+ State createState() => SsupportStateScreen();
+}
+
+class SsupportStateScreen extends State {
+ SettingModel? settings;
+
+ 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')}';
+
+ if (!await launch(_whatsappUrl)) {
+ throw Exception('Could not launch $_whatsappUrl');
+ }
+ }
+
+ Future _sendEmail(String recipients) async {
+ final Email email = Email(
+ body: '',
+ subject: 'Soporte Prosapp',
+ recipients: [recipients],
+ isHTML: false,
+ );
+
+ await FlutterEmailSender.send(email);
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ if (settings == null) {
+ SettingModel.getSettings().then(
+ (SettingModel value) => setState(() {
+ settings = value;
+ }),
+ );
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: PopAppbar(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ label: 'Soporte'),
+ body: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 30, left: 35, right: 35),
+ child: Text(
+ '${settings?.titulo}',
+ style: const TextStyle(fontWeight: FontWeight.w600),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 35),
+ child: Text('${settings?.parrafo}'),
+ ),
+ Row(
+ children: [
+ const Expanded(child: SizedBox()),
+ ElevatedButton(
+ onPressed: () {
+ _sendWhatsapp(settings?.numero);
+ },
+ style: ElevatedButton.styleFrom(
+ foregroundColor: Colors.white,
+ backgroundColor: const Color(0xFF2BA4EC),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ side: const BorderSide(
+ color: Color(0xFF2BA4EC),
+ width: 2,
+ ),
+ ),
+ ),
+ child: const Padding(
+ padding: EdgeInsets.symmetric(vertical: 18, horizontal: 0),
+ child: Icon(
+ CommunityMaterialIcons.whatsapp,
+ size: 30,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ const SizedBox(width: 20),
+ ElevatedButton(
+ onPressed: () {
+ _sendEmail(settings?.email ?? '');
+ },
+ style: ElevatedButton.styleFrom(
+ foregroundColor: Colors.white,
+ backgroundColor: const Color(0xFF2BA4EC),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(50),
+ side: const BorderSide(
+ color: Color(0xFF2BA4EC),
+ width: 2,
+ ),
+ ),
+ ),
+ child: const Padding(
+ padding: EdgeInsets.symmetric(vertical: 18, horizontal: 0),
+ child: Icon(
+ Icons.email_outlined,
+ size: 30,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ const Expanded(child: SizedBox()),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 50),
+ child: Column(
+ children: [
+ const Text(
+ 'Horario de atención:',
+ style: TextStyle(fontWeight: FontWeight.w600),
+ ),
+ const SizedBox(height: 20),
+ Text('${settings?.dias}'),
+ const SizedBox(height: 5),
+ Text('${settings?.horas}'),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/src/screens/web_view.dart b/lib/src/screens/web_view.dart
new file mode 100644
index 0000000..6fc390e
--- /dev/null
+++ b/lib/src/screens/web_view.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:prosappco/src/components/pop_appbar.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+
+class WebViewScreen extends StatefulWidget {
+ String label;
+ String link;
+ WebViewScreen({super.key, required this.label, required this.link});
+
+ @override
+ State createState() => _WebViewScreenState();
+}
+
+class _WebViewScreenState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setBackgroundColor(const Color(0x00000000))
+ ..setNavigationDelegate(
+ NavigationDelegate(
+ onProgress: (int progress) {
+ // Update loading bar.
+ },
+ onPageStarted: (String url) {},
+ onPageFinished: (String url) {},
+ onWebResourceError: (WebResourceError error) {},
+ onNavigationRequest: (NavigationRequest request) {
+ if (request.url.startsWith('https://www.youtube.com/')) {
+ return NavigationDecision.prevent;
+ }
+ return NavigationDecision.navigate;
+ },
+ ),
+ )
+ ..loadRequest(Uri.parse('${widget.link}'));
+ return Scaffold(
+ appBar: PopAppbar(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ label: widget.label,
+ ),
+ body: WebViewWidget(controller: controller),
+ );
+ }
+}
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 9c4e7a1..2ef86b8 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -8,6 +8,7 @@ import Foundation
import cloud_firestore
import firebase_auth
import firebase_core
+import firebase_messaging
import firebase_storage
import geolocator_apple
import url_launcher_macos
@@ -16,6 +17,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
+ FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
diff --git a/pubspec.lock b/pubspec.lock
index 25ddfc5..379a90f 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _flutterfire_internals
- sha256: f175bc1414e4edf8c5b83372c98eeabecf8353f39c9da423c2cfdf1f1f508788
+ sha256: "867b77e2367bc502dcd4d5a66302615409f04eb20ed82ba1c0ba073f9107e018"
url: "https://pub.dev"
source: hosted
- version: "1.1.0"
+ version: "1.2.0"
async:
dependency: transitive
description:
@@ -141,26 +141,50 @@ packages:
dependency: "direct main"
description:
name: firebase_core
- sha256: ed611fb8e67e43ecc7956f242cecca383d87cf71aace27287aa5dd4bdba4ac07
+ sha256: dcf54c170c5371ad0e79229d0fb372c58262ae0968b7de222bf28e51dd236be0
url: "https://pub.dev"
source: hosted
- version: "2.9.0"
+ version: "2.11.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
- sha256: "0df0a064ab0cad7f8836291ca6f3272edd7b83ad5b3540478ee46a0849d8022b"
+ sha256: ae79f335f6c7f2dadb00c98c429da2ca905d265e0225fb5e7dfa62ac3accad48
url: "https://pub.dev"
source: hosted
- version: "4.6.0"
+ version: "4.7.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
- sha256: "347351a8f0518f3343d79a9a0690fa67ad232fc32e2ea270677791949eac792b"
+ sha256: e57ef862257a0d977c1308d02e2fbb9b68525e6d85711b08f3df8cec836fb444
url: "https://pub.dev"
source: hosted
- version: "2.3.0"
+ version: "2.4.0"
+ firebase_messaging:
+ dependency: "direct main"
+ description:
+ name: firebase_messaging
+ sha256: "88a4bf569899344d863db26a73092681b590274f570f0a431051b92c4dd9bee3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.5.0"
+ firebase_messaging_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_messaging_platform_interface
+ sha256: "482fa6998f6cb10b30324ca8bfa0e2fcc0defed9484ccccce118b1b347e39a24"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.4.0"
+ firebase_messaging_web:
+ dependency: transitive
+ description:
+ name: firebase_messaging_web
+ sha256: "61f7d323085704fbcc7b056a49d75211b2eb698610b878fec6cda18dd04d754f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.4.0"
firebase_storage:
dependency: "direct main"
description:
@@ -198,6 +222,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.1+1"
+ flutter_email_sender:
+ dependency: "direct main"
+ description:
+ name: flutter_email_sender
+ sha256: "9e253c69617f43d4cb5de672e93a7a19c12a21fb6a75e66c6ce7626336c4c1bc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.2.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -725,6 +757,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
+ webview_flutter:
+ dependency: "direct main"
+ description:
+ name: webview_flutter
+ sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.0"
+ webview_flutter_android:
+ dependency: transitive
+ description:
+ name: webview_flutter_android
+ sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.7.0"
+ webview_flutter_platform_interface:
+ dependency: transitive
+ description:
+ name: webview_flutter_platform_interface
+ sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ webview_flutter_wkwebview:
+ dependency: transitive
+ description:
+ name: webview_flutter_wkwebview
+ sha256: "61f33512810bf1ee9ac89761a4b02663ff64e8227b7dc80654642acd660fd49d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.4.2"
sdks:
dart: ">=2.19.3 <3.0.0"
flutter: ">=3.3.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index a5ec4e1..0682bed 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -56,8 +56,11 @@ dependencies:
google_maps_flutter: ^2.2.5
geolocator: ^9.0.2
geocoding: ^2.1.0
- url_launcher: ^6.0.3
+ url_launcher: ^6.1.10
community_material_icon: ^5.9.55
+ flutter_email_sender: ^5.2.0
+ webview_flutter: ^4.2.0
+ firebase_messaging: ^14.5.0
dev_dependencies:
flutter_test:
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index 94586cc..16039c1 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h"
+#include
#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
+ FirebaseCorePluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index f0bcafd..e30096f 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ firebase_core
geolocator_windows
url_launcher_windows
)