chat primera face

This commit is contained in:
Juan Felipe Duarte
2023-05-03 17:07:26 -05:00
parent f1571e6374
commit 1a0e33328e
16 changed files with 785 additions and 95 deletions
+8 -5
View File
@@ -61,9 +61,10 @@ class _CalendarScreenState extends State<CalendarScreen> {
super.initState();
if (_events == null) {
Event.getEventsAllById(uid ?? "").then((value) => setState(() {
_events = value;
}));
Event.getEventsAllByIdStatus(uid ?? "", 'aprobado')
.then((value) => setState(() {
_events = value;
}));
}
}
@@ -293,14 +294,16 @@ class _CalendarScreenState extends State<CalendarScreen> {
'',
'',
0,
0)
0,
'aprobado')
.then((value) {
Navigator.pop(context);
_titleController.text = '';
_descriptionController.text = '';
});
Event.getEventsAllById(uid ?? "").then(
Event.getEventsAllByIdStatus(uid ?? "", 'aprobado')
.then(
(value) => setState(() {
_events = value;
}),
+109
View File
@@ -0,0 +1,109 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/chat_model.dart';
class ChatScreen extends StatefulWidget {
final String eventoId;
const ChatScreen({super.key, required this.eventoId});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _textController = TextEditingController();
final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
Widget build(BuildContext context) {
String textoIngresado = _textController.text;
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Mensajes'),
body: Column(
children: [
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chats')
.doc(widget.eventoId)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final data = snapshot.data!;
final chat = ChatModel.fromDocumentSnapshot(data);
print('messages ${chat.messages}');
return Column(children: [
...chat.messages.map(
(e) => ListTile(
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${e.content}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
// TextSpan(
// text: DateFormat('dd MMM', 'es')
// .format(DateTime.parse(e.day)),
// style: const TextStyle(
// color: Colors.grey,
// fontSize: 16,
// ),
// ),
],
),
),
),
)
]);
// ListTile(
// title: Text('${}'),
// );
},
),
TextFormField(
controller: _textController,
),
IconButton(
onPressed: () async {
final nuevoMensaje = MessageModel(
user: uid!,
content: _textController.text,
timestamp: DateTime.now());
final nuevoMensajeMap = {
'user': nuevoMensaje.user,
'content': nuevoMensaje.content,
'timestamp': nuevoMensaje.timestamp,
};
FirebaseFirestore.instance
.collection('chats')
.doc(widget.eventoId)
.update({
'message': FieldValue.arrayUnion([nuevoMensajeMap])
});
_textController.clear();
},
icon: const Icon(Icons.send))
],
),
);
}
}
+108 -23
View File
@@ -1,5 +1,7 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
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:intl/intl.dart';
@@ -8,6 +10,7 @@ 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';
import 'package:prosappco/src/screens/chat.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:community_material_icon/community_material_icon.dart';
@@ -213,6 +216,44 @@ class _CitaScreenState extends State<CitaScreen> {
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () async {
await FirebaseFirestore.instance
.collection('chats')
.doc(widget.evento.id)
.set({});
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ChatScreen(eventoId: widget.evento.id!);
},
),
);
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
),
child: const Padding(
padding:
EdgeInsets.symmetric(vertical: 18, horizontal: 0),
child: Icon(
Icons.message,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
_sendWhatsapp();
@@ -280,32 +321,76 @@ class _CitaScreenState extends State<CitaScreen> {
child: Text('${widget.evento.address}'))
],
),
)
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: ElevatedButton(
onPressed: () {},
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,
),
),
),
),
widget.evento.status == 'pendiendo'
? 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": "aprobado"}).then((value) {
Navigator.pushReplacementNamed(
context, '/solicitud');
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Aceptar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
),
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(),
],
),
);
+151
View File
@@ -0,0 +1,151 @@
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/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class MyServicesScreen extends StatefulWidget {
MyServicesScreen({super.key});
@override
State<MyServicesScreen> createState() => _MyServicesScreenState();
}
class _MyServicesScreenState extends State<MyServicesScreen> {
DateTime today = DateTime.now();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Mis servicios'),
drawer: DrawerProfessional(),
body: SingleChildScrollView(
child: Column(
children: [
_eventList(),
],
),
),
),
);
}
Widget _eventList() {
return FutureBuilder(
future: getByProIdAll('aprobado', 'denegado'),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
List<Event> eventos = [];
try {
eventos.addAll(snapshot.data!);
} catch (e) {
print("Error" + e.toString());
}
if (eventos.isEmpty) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 50),
child: Center(child: Text('No tienes citas')),
);
}
return Column(
children: [
...eventos.map(
(event) => ListTile(
tileColor: event.status == 'aprobado'
? Colors.green[100]
: Colors.red[100],
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
),
);
},
leading: Text(event.range1Hour1),
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${event.title}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
TextSpan(
text: DateFormat('dd MMM', 'es')
.format(DateTime.parse(event.day)),
style: const TextStyle(
color: Colors.grey,
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.toString()}'),
],
),
Text(
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],
),
trailing: const Icon(Icons.keyboard_arrow_right),
),
),
],
);
},
);
}
}
+161
View File
@@ -0,0 +1,161 @@
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/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class MyServicesProScreen extends StatefulWidget {
MyServicesProScreen({super.key});
@override
State<MyServicesProScreen> createState() => _MyServicesProScreenState();
}
class _MyServicesProScreenState extends State<MyServicesProScreen> {
DateTime today = DateTime.now();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Mis servicios'),
drawer: DrawerProfessional(),
body: SingleChildScrollView(
child: Column(
children: [
_eventList(),
],
),
),
),
);
}
Widget _eventList() {
return FutureBuilder(
future: getByProIdAll('aprobado', 'denegado'),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
List<Event> eventos = [];
try {
eventos.addAll(snapshot.data!);
} catch (e) {
print("Error" + e.toString());
}
if (eventos.isEmpty) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 50),
child: Center(child: Text('No tienes citas')),
);
}
return Column(
children: [
...eventos.map(
(event) => ListTile(
tileColor: event.status == 'aprobado'
? Colors.blue[100]
: Colors.red[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,
)
: 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,
),
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}',
style: const TextStyle(
color: Colors.grey,
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.toString()}'),
],
),
Text(
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],
),
trailing: const Icon(Icons.keyboard_arrow_right),
),
),
],
);
},
);
}
}
+30 -26
View File
@@ -3,7 +3,6 @@ import 'package:diacritic/diacritic.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.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';
@@ -171,28 +170,30 @@ class _ProfessionalScreenState extends State<ProfessionalScreen> {
_photo = await AuthenticationRepository.instance.getPhoto(user.id);
if (data['estado'] == 'activo') {
List<String> especializaciones;
if (user.id != uid) {
List<String> especializaciones;
especializaciones = (data['especializaciones'] as List<dynamic>)
.map((e) => e.toString())
.toList();
especializaciones = (data['especializaciones'] as List<dynamic>)
.map((e) => e.toString())
.toList();
if (widget.profession == data['profesion'] ||
widget.profession == '') {
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.scoreTo(user.id, true, true),
);
professionals.add(professional);
if (widget.profession == data['profesion'] ||
widget.profession == '') {
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.scoreTo(user.id, true, true),
);
professionals.add(professional);
}
}
}
}
@@ -211,13 +212,16 @@ class _ProfessionalScreenState extends State<ProfessionalScreen> {
setState(() {
if (_professionals != null) {
if (searchController.text.isEmpty) {
filteredProfessionals = _professionals!;
filteredProfessionals = _professionals!
.where((professional) => professional.id != uid)
.toList();
} else {
filteredProfessionals = _professionals!
.where((professional) => removeDiacritics(professional.name)
.toLowerCase()
.contains(
removeDiacritics(searchController.text.toLowerCase())))
.where((professional) =>
removeDiacritics(professional.name).toLowerCase().contains(
removeDiacritics(
searchController.text.toLowerCase())) &&
professional.id != uid)
.toList();
}
}
+20 -13
View File
@@ -1,7 +1,6 @@
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -9,10 +8,10 @@ import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/banner_photo.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/components/schedule_picker.dart';
import 'package:prosappco/src/models/setting_model.dart';
import 'package:prosappco/src/screens/horario.dart';
import 'package:prosappco/src/screens/professional.dart';
import 'package:prosappco/src/screens/professional_direccion.dart';
@@ -41,12 +40,18 @@ class _ProfileProState extends State<ProfilePro> {
var _direccion = '...';
var _ubicacion = '...';
var _opcionalAddress = '...';
SettingModel? setting_domicilio;
Map<String, Schedule>? _horarios;
@override
void initState() {
super.initState();
if (setting_domicilio == null) {
SettingModel.getDomiciliosSettings().then(
(SettingModel value) => setState(() => setting_domicilio = value),
);
}
final uid = AuthenticationRepository.instance.getCurrentUserUid();
@@ -283,17 +288,19 @@ class _ProfileProState extends State<ProfilePro> {
),
),
),
const Divider(
color: Colors.white,
height: 12,
),
customSwitch(
'Servicio a domicilio',
domicilioValue,
(value) {
domicilioValue = value;
},
),
if (setting_domicilio?.domicilios == true)
const Divider(
color: Colors.white,
height: 12,
),
if (setting_domicilio?.domicilios == true)
customSwitch(
'Servicio a domicilio',
domicilioValue,
(value) {
domicilioValue = value;
},
),
const Divider(),
customSwitch(
'Servicio en sitio',
+73 -18
View File
@@ -1,4 +1,5 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:community_material_icon/community_material_icon.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@@ -88,7 +89,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: now,
firstDate: DateTime(now.year),
firstDate: now,
lastDate: DateTime(now.year + 1),
// builder: (context, child) {
// return Theme(data: ThemeData.dark(), child: child!);
@@ -130,10 +131,19 @@ class _ServiceScreenState extends State<ServiceScreen> {
final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid();
BitmapDescriptor? _markerIcon;
@override
void initState() {
super.initState();
BitmapDescriptor.fromAssetImage(
ImageConfiguration(size: Size(48, 48)), 'images/pro_marke.png')
.then((icon) {
setState(() {
_markerIcon = icon;
});
});
try {
getUsersWithActiveStatus().then((value) {
for (var doc in value) {
@@ -142,6 +152,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
if (element['latitude'] != null && element['longitude'] != null) {
markers.add(
Marker(
icon: _markerIcon!,
markerId: MarkerId(doc.id),
position: LatLng(
element['latitude'],
@@ -448,7 +459,29 @@ class _ServiceScreenState extends State<ServiceScreen> {
width: 180,
child: TextFormField(
onTap: () {
_selectDate(context);
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(
@@ -469,7 +502,29 @@ class _ServiceScreenState extends State<ServiceScreen> {
width: 140,
child: TextFormField(
onTap: () {
_selectTime(context);
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(
@@ -508,21 +563,21 @@ class _ServiceScreenState extends State<ServiceScreen> {
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,
)
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')
.then((value) {
Navigator.push(
context,
+3 -4
View File
@@ -4,7 +4,6 @@ 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 {
@@ -52,7 +51,7 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
Widget _eventList() {
return FutureBuilder(
future: getByProIdAll(),
future: getByProIdAll('pendiente', ''),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
return const Center(
@@ -135,11 +134,11 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
],
),
Text(
'" ${event.description} "',
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],