This commit is contained in:
Juan Felipe Duarte
2023-04-29 17:56:29 -05:00
parent 0f90bc7ee9
commit 5baf019198
6 changed files with 460 additions and 99 deletions
+68 -17
View File
@@ -5,25 +5,48 @@ import 'package:prosappco/src/models/scores_model.dart';
final uid = AuthenticationRepository.instance.getCurrentUserUid();
class EventoService {
Future<void> createEvent(String title, String description, String day,
String range1Hour1, String range1Hour2, String professionalId) async {
Future<String?> createEvent(
String title,
String description,
String day,
String range1Hour1,
String range1Hour2,
String professionalId,
String ubicacion,
String address,
double latitude,
double longitude,
) async {
try {
await FirebaseFirestore.instance.collection('services').add({
DateTime ahora = DateTime.now();
final eventId =
await FirebaseFirestore.instance.collection('services').add({
'user_id': uid,
'title': title,
'description': description,
'day': day,
'range1Hour1': range1Hour1,
'range1Hour2': range1Hour2,
'professional_id': professionalId
'professional_id': professionalId,
'ubicacion': ubicacion,
'address': address,
'latitude': latitude,
'longitude': longitude,
'Timestamp': ahora
}).then((value) {
FirebaseFirestore.instance.collection('users').doc(uid).update({
'services': FieldValue.arrayUnion([value.id])
});
return value.id;
});
return eventId;
} catch (e) {
print('Evento $e');
}
return null;
}
}
@@ -37,7 +60,9 @@ Future<List<Event>> getByProId(String day) async {
List<Event> eventos = [];
for (var element in snapshot.docs) {
eventos.add(Event.fromJson(element.data()));
final event = Event.fromJson(element.data());
event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false);
eventos.add(event);
}
print('eventos $eventos');
return eventos;
@@ -57,8 +82,7 @@ Future<List<Event>> getByProIdAll() async {
List<Event> eventos = [];
for (var element in snapshot.docs) {
final event = Event.fromJson(element.data());
event.scoresModel =
await ScoresModel.scoreTo(event.userId ?? "", false, false);
event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false);
eventos.add(event);
}
print('eventos $eventos');
@@ -70,23 +94,31 @@ Future<List<Event>> getByProIdAll() async {
}
class Event {
String? title;
String title;
String? description;
String? day;
String? range1Hour1;
String day;
String range1Hour1;
String? range1Hour2;
String? userId;
String? professionalId;
String userId;
String professionalId;
String? ubicacion;
String? address;
double? longitud;
double? latitud;
ScoresModel? scoresModel;
Event({
this.title,
required this.title,
this.description,
this.day,
this.range1Hour1,
required this.day,
required this.range1Hour1,
this.range1Hour2,
this.userId,
this.professionalId,
required this.userId,
required this.professionalId,
this.ubicacion,
this.address,
this.longitud,
this.latitud,
});
factory Event.fromJson(Map<String, dynamic> json) {
@@ -98,9 +130,28 @@ class Event {
range1Hour2: json['range1Hour2'],
userId: json['user_id'],
professionalId: json['professional_id'],
ubicacion: json['ubicacion'] ?? '',
address: json['address'] ?? '',
longitud: json['longitude'] ?? 0,
latitud: json['latitude'] ?? 0,
);
}
static Future<Event> getEventById(String uid) async {
try {
final snapshot = await FirebaseFirestore.instance
.collection('services')
.doc(uid)
.get();
final Map<String, dynamic>? data = snapshot.data();
return Event.fromJson(data!);
} catch (e) {
print('Error getting user: $e');
return Event(
title: '', day: '', range1Hour1: '', userId: '', professionalId: '');
}
}
static Future<List<Event>> getEventsAllById(String uid) async {
try {
var snapshot = await FirebaseFirestore.instance
+16 -29
View File
@@ -24,8 +24,6 @@ class _CalendarScreenState extends State<CalendarScreen> {
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
ScoresModel? scoresModel;
EventoService eventoService = EventoService();
CalendarFormat _calendarFormat = CalendarFormat.month;
DateTime today = DateTime.now();
@@ -67,12 +65,6 @@ class _CalendarScreenState extends State<CalendarScreen> {
_events = value;
}));
}
if (scoresModel == null) {
ScoresModel.scoreFrom(uid.toString(), true, false).then(
(ScoresModel s) => setState(() => scoresModel = s),
);
}
}
void _onDaySelected(DateTime day, DateTime focusedDay) {
@@ -124,7 +116,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
eventLoader: (date) {
return events
.where((element) {
DateTime day = DateTime.parse(element.day ?? "");
DateTime day = DateTime.parse(element.day);
return (date.year == day.year &&
date.month == day.month &&
date.day == day.day);
@@ -292,13 +284,16 @@ class _CalendarScreenState extends State<CalendarScreen> {
onPressed: () async {
await eventoService
.createEvent(
_titleController.text,
_descriptionController.text,
today.toString(),
_selectedTime1!.format(context).toString(),
_selectedTime2!.format(context).toString(),
uid.toString(),
)
_titleController.text,
_descriptionController.text,
today.toString(),
_selectedTime1!.format(context).toString(),
_selectedTime2!.format(context).toString(),
uid.toString(),
'',
'',
0,
0)
.then((value) {
Navigator.pop(context);
_titleController.text = '';
@@ -348,24 +343,16 @@ class _CalendarScreenState extends State<CalendarScreen> {
...eventos.map(
(e) => ListTile(
onTap: () {
final evento = Event(
day: e.day,
description: e.description,
userId: e.userId,
range1Hour1: e.range1Hour1,
range1Hour2: e.range1Hour2,
title: e.title,
);
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: evento);
return CitaScreen(evento: e);
},
),
);
},
leading: Text('${e.range1Hour1}'),
leading: Text(e.range1Hour1),
title: RichText(
text: TextSpan(
children: [
@@ -379,7 +366,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
),
TextSpan(
text: DateFormat('dd MMM', 'es')
.format(DateTime.parse(e.day!)),
.format(DateTime.parse(e.day)),
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
@@ -394,7 +381,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
Row(
children: [
RatingBar.builder(
initialRating: scoresModel?.average ?? 0,
initialRating: e.scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
@@ -412,7 +399,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
),
const SizedBox(width: 5),
Text(
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
'(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toString()}'),
],
),
Text(
+108 -2
View File
@@ -39,7 +39,7 @@ class _CitaScreenState extends State<CitaScreen> {
Widget build(BuildContext context) {
if (nombre == '') {
UserModel.getUser(widget.evento.userId!).then((value) {
UserModel.getUser(widget.evento.userId).then((value) {
setState(() {
nombre = value.name;
ref_photo = value.photo;
@@ -78,7 +78,7 @@ class _CitaScreenState extends State<CitaScreen> {
),
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))} ${widget.evento.range1Hour1}',
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
@@ -111,11 +111,117 @@ class _CitaScreenState extends State<CitaScreen> {
],
),
),
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),
),
],
),
),
Text(
textAlign: TextAlign.center,
'" ${widget.evento.description} "',
style: const TextStyle(
color: Colors.grey, fontStyle: FontStyle.italic),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(
'Medios de comunicación con el usuario.',
style: TextStyle(color: Color(0xFF2BA4EC)),
),
),
Row(
children: [
const Expanded(child: SizedBox()),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: const Color(0xFF2BA4EC), width: 2),
borderRadius: BorderRadius.circular(50),
),
child: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.white,
elevation: 0,
child: const Icon(Icons.phone_android,
size: 30, color: Color(0xFF2BA4EC)),
),
),
const SizedBox(width: 20),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: const Color(0xFF2BA4EC), width: 2),
borderRadius: BorderRadius.circular(50),
),
child: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.white,
elevation: 0,
child: const Icon(Icons.message,
size: 30, color: Color(0xFF2BA4EC)),
),
),
const Expanded(child: SizedBox()),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: const Color(0xFF2BA4EC),
border: Border.all(
color: const Color(0xFF2BA4EC), width: 2),
borderRadius: BorderRadius.circular(50),
),
child: FloatingActionButton(
onPressed: () {},
backgroundColor: const Color(0xFF2BA4EC),
elevation: 0,
child: const Icon(Icons.near_me,
size: 30, color: Colors.white),
),
),
Text('${widget.evento.address}')
],
),
)
],
),
+94 -48
View File
@@ -1,3 +1,4 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@@ -27,7 +28,8 @@ class _ServiceScreenState extends State<ServiceScreen> {
EventoService eventoService = EventoService();
final TextEditingController _locationController = TextEditingController();
final String _locationPosition = '';
String _locationPosition = '';
String ubicacion = '';
final _profesionalController = TextEditingController();
final _serviceTypeController = TextEditingController();
final _observacionController = TextEditingController();
@@ -115,12 +117,44 @@ class _ServiceScreenState extends State<ServiceScreen> {
}
}
Future<List<DocumentSnapshot<Map<String, dynamic>>>>
getUsersWithActiveStatus() async {
var querySnapshot = await FirebaseFirestore.instance
.collection('users')
.where('estado', isEqualTo: 'activo')
.get();
return querySnapshot.docs;
}
final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
void initState() {
super.initState();
try {
getUsersWithActiveStatus().then((value) {
for (var doc in value) {
final element = doc.data()!;
if (element['latitude'] != null && element['longitude'] != null) {
markers.add(
Marker(
markerId: MarkerId(doc.id),
position: LatLng(
element['latitude'],
element['longitude'],
),
),
);
}
}
});
} catch (e) {
print('error coordenadas $e');
}
}
late String lat;
@@ -191,14 +225,14 @@ class _ServiceScreenState extends State<ServiceScreen> {
),
),
const Positioned(
bottom: 10,
bottom: 25,
right: 0,
left: 0,
top: 0,
child: Icon(
Icons.location_on,
size: 40,
color: Colors.red,
color: Color(0xFFFF0000),
),
),
Positioned(
@@ -328,13 +362,17 @@ class _ServiceScreenState extends State<ServiceScreen> {
child: Form(
child: Column(
children: [
TextFormField(
readOnly: true,
controller: _locationController,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.near_me),
hintText: 'Dirección',
),
Column(
children: [
TextFormField(
readOnly: true,
controller: _locationController,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.near_me),
hintText: 'Dirección',
),
),
],
),
const SizedBox(height: 15),
TextFormField(
@@ -353,49 +391,45 @@ class _ServiceScreenState extends State<ServiceScreen> {
);
if (datos != null) {
Professional? profesional = datos[0];
ubicacion = datos[1];
if (profesional != null) {
setState(() {
_profesionalController.text =
profesional.name.toString();
professionalAddress =
profesional.realAddress;
professionalUbicacion =
profesional.ubicacion;
_locationController.text =
profesional.realAddress;
professionalLatitude =
profesional.latitude;
professionalLongitude =
profesional.longitude;
if (professionalLatitude != null &&
professionalLatitude != 0 &&
professionalLongitude != null &&
professionalLongitude != 0) {
markers.clear();
if (ubicacion == 'sitio') {
professionalAddress =
profesional.realAddress;
markers.add(Marker(
markerId:
const MarkerId('professional'),
position: LatLng(
professionalLatitude!,
professionalLongitude!)));
googleMapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
professionalLatitude!,
professionalLongitude!,
),
zoom: 17),
),
);
_locationController.text =
profesional.realAddress;
if (professionalLatitude != null &&
professionalLatitude != 0 &&
professionalLongitude != null &&
professionalLongitude != 0) {
googleMapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
professionalLatitude!,
professionalLongitude!,
),
zoom: 17),
),
);
}
}
professionalId = profesional.id;
});
}
@@ -472,24 +506,36 @@ class _ServiceScreenState extends State<ServiceScreen> {
TimeOfDay(hour: hora2, minute: minutos2);
UserModel.getUser(uid.toString()).then((value) {
eventoService.createEvent(
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,
)
.then((value) {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ServiceAfterScreen(
eventoId: value,
);
},
),
);
});
});
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (BuildContext context) {
// return const ServiceAfterScreen();
// },
// ),
// );
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
+172 -1
View File
@@ -1,19 +1,190 @@
import 'package:cloud_firestore/cloud_firestore.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/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
class ServiceAfterScreen extends StatefulWidget {
const ServiceAfterScreen({super.key});
var eventoId;
ServiceAfterScreen({super.key, this.eventoId});
@override
State<ServiceAfterScreen> createState() => _ServiceAfterScreenState();
}
class _ServiceAfterScreenState extends State<ServiceAfterScreen> {
Event? evento;
UserModel? user;
ScoresModel? scoresModel;
@override
void initState() {
super.initState();
Event.getEventById(widget.eventoId).then((value) {
setState(() {
evento = value;
});
if (scoresModel == null) {
ScoresModel.scoreTo(value.professionalId, true, false).then(
(ScoresModel s) => setState(() {
scoresModel = s;
}),
);
}
UserModel.getUser(value.professionalId).then((s) {
setState(
() => user = s,
);
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:
PopAppbar(onPressed: () => Navigator.pop(context), label: 'Servicio'),
body: Column(
children: [
ListTile(
leading: ReferencePhoto(
ref: user?.photo,
size: 55,
sizeCircle: 60,
),
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${user?.name}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 16,
),
),
TextSpan(
text: DateFormat('dd MMM', 'es').format(DateTime.parse(
evento?.day ?? '2023-01-01 00:00:00.000Z')),
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
),
),
TextSpan(
text: ' ${evento?.range1Hour1}',
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
),
),
],
),
),
subtitle: Column(
children: [
Row(
children: [
RatingBar.builder(
initialRating: scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemPadding: const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
],
),
],
),
),
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),
evento?.ubicacion != 'sitio'
? const Text(
'Servicio a su domicilio.',
style: TextStyle(color: Colors.black, fontSize: 14),
)
: const Text(
'Servicio en sitio / consultorio',
style: TextStyle(color: Colors.black, fontSize: 14),
),
],
),
),
ListTile(
leading: const Icon(Icons.near_me),
title: Text(
'${evento?.address}',
style: TextStyle(fontSize: 15, color: Colors.grey[600]),
),
),
Text(
'" ${evento?.description} "',
style:
TextStyle(color: Colors.grey[600], fontStyle: FontStyle.italic),
),
Center(
child: Column(
children: const [
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Icon(
Icons.check_circle_outline_rounded,
color: Color(0xFF35A8ED),
size: 70,
),
),
Text(
'Servicio solicitado exitosamente',
style: TextStyle(
color: Color(0xFF35A8ED),
fontSize: 17,
fontWeight: FontWeight.w600),
),
],
),
),
],
),
);
}
}
+2 -2
View File
@@ -88,7 +88,7 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
),
);
},
leading: Text('${event.range1Hour1}'),
leading: Text(event.range1Hour1),
title: RichText(
text: TextSpan(
children: [
@@ -102,7 +102,7 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
),
TextSpan(
text: DateFormat('dd MMM', 'es')
.format(DateTime.parse(event.day!)),
.format(DateTime.parse(event.day)),
style: const TextStyle(
color: Colors.grey,
fontSize: 16,