validaciones de inputs

This commit is contained in:
Juan Felipe Duarte
2023-05-12 16:56:43 -05:00
parent d5702207d2
commit 4b1e214742
23 changed files with 8566 additions and 207 deletions
+5
View File
@@ -0,0 +1,5 @@
{
"projects": {
"default": "prosapp-5747a"
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
]
}
+32
View File
@@ -0,0 +1,32 @@
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"google",
"plugin:@typescript-eslint/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["tsconfig.json", "tsconfig.dev.json"],
sourceType: "module",
},
ignorePatterns: [
"/lib/**/*", // Ignore built files.
],
plugins: [
"@typescript-eslint",
"import",
],
rules: {
"quotes": ["error", "double"],
"import/no-unresolved": 0,
"indent": ["error", 2],
},
};
+9
View File
@@ -0,0 +1,9 @@
# Compiled JavaScript files
lib/**/*.js
lib/**/*.js.map
# TypeScript v1 declaration files
typings/
# Node.js dependency directory
node_modules/
+7882
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "functions",
"scripts": {
"lint": "eslint --ext .js,.ts .",
"build": "tsc",
"build:watch": "tsc --watch",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "18"
},
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^11.8.0",
"firebase-functions": "^4.3.1"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"eslint": "^8.9.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-import": "^2.25.4",
"firebase-functions-test": "^3.1.0",
"typescript": "^4.9.0"
},
"private": true
}
+36
View File
@@ -0,0 +1,36 @@
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const fcm = admin.messaging();
exports.checkhealth = functions.https.onCall(async (data, context) => {
return "The function is online";
});
exports.sendNotification = functions.https.onCall(async (data, context) => {
const title = data.title;
const body = data.body;
const token = data.token;
try {
const payload = {
token: token,
notification: {
title: title,
body: body,
},
data: {
body: body,
},
};
return fcm.send(payload).then((response) => {
return {success: true, response: "Succefully sent message: " + response};
}).catch((error) => {
return {error: error};
});
} catch (error) {
throw new functions.https.HttpsError("invalid-argument", "error:" + error);
}
});
+5
View File
@@ -0,0 +1,5 @@
{
"include": [
".eslintrc.js"
]
}
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017"
},
"compileOnSave": true,
"include": [
"src"
]
}
+2 -3
View File
@@ -11,8 +11,7 @@ import 'package:prosappco/src/screens/my_services_pro.dart';
import 'package:prosappco/src/screens/professional.dart'; import 'package:prosappco/src/screens/professional.dart';
import 'package:prosappco/src/screens/profile.dart'; import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/profile_pro.dart'; import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/reputation.dart'; import 'package:prosappco/src/screens/reputacion_pro.dart';
import '../models/scores_model.dart'; import '../models/scores_model.dart';
import '../screens/configuracion.dart'; import '../screens/configuracion.dart';
import '../screens/support.dart'; import '../screens/support.dart';
@@ -284,7 +283,7 @@ class _DrawerProfessionalState extends State<DrawerProfessional> {
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (BuildContext context) { builder: (BuildContext context) {
return ReputationScreen(); return ReputationProScreen();
}, },
), ),
); );
+10
View File
@@ -18,6 +18,8 @@ class EventoService {
double longitude, double longitude,
String status, String status,
int? tarifa, int? tarifa,
bool professionalScored,
bool userScored,
) async { ) async {
try { try {
DateTime ahora = DateTime.now(); DateTime ahora = DateTime.now();
@@ -38,6 +40,8 @@ class EventoService {
'status': status, 'status': status,
'Timestamp': ahora, 'Timestamp': ahora,
'tarifa': tarifa ?? 0, 'tarifa': tarifa ?? 0,
'professional_scored': professionalScored,
'user_scored': userScored,
}).then((value) { }).then((value) {
FirebaseFirestore.instance.collection('users').doc(uid).update({ FirebaseFirestore.instance.collection('users').doc(uid).update({
'services': FieldValue.arrayUnion([value.id]) 'services': FieldValue.arrayUnion([value.id])
@@ -138,6 +142,8 @@ class Event {
String status; String status;
ScoresModel? scoresModel; ScoresModel? scoresModel;
int? tarifa; int? tarifa;
bool professionalScored;
bool userScored;
Event({ Event({
this.id, this.id,
@@ -154,6 +160,8 @@ class Event {
this.latitud, this.latitud,
this.status = 'pendiente', this.status = 'pendiente',
this.tarifa, this.tarifa,
this.professionalScored = false,
this.userScored = false,
}); });
factory Event.fromJson(Map<String, dynamic> json) { factory Event.fromJson(Map<String, dynamic> json) {
@@ -172,6 +180,8 @@ class Event {
latitud: json['latitude'] ?? 0, latitud: json['latitude'] ?? 0,
status: json['status'], status: json['status'],
tarifa: json['tarifa'] ?? 0, tarifa: json['tarifa'] ?? 0,
professionalScored: json['professional_scored'],
userScored: json['user_scored'],
); );
} }
+2
View File
@@ -315,6 +315,8 @@ class _CalendarScreenState extends State<CalendarScreen> {
0, 0,
'aprobado', 'aprobado',
0, 0,
false,
false,
) )
.then((value) { .then((value) {
Navigator.pop(context); Navigator.pop(context);
+23 -17
View File
@@ -124,25 +124,29 @@ class _ChatScreenState extends State<ChatScreen> {
const SizedBox(width: 12), const SizedBox(width: 12),
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
final nuevoMensaje = MessageModel( String muestra = _textController.text.trim();
user: uid!, if (muestra.isNotEmpty) {
content: _textController.text, final nuevoMensaje = MessageModel(
timestamp: DateTime.now()); user: uid!,
content:
_textController.text.trimLeft().trimRight(),
timestamp: DateTime.now());
final nuevoMensajeMap = { final nuevoMensajeMap = {
'user': nuevoMensaje.user, 'user': nuevoMensaje.user,
'content': nuevoMensaje.content, 'content': nuevoMensaje.content,
'timestamp': nuevoMensaje.timestamp, 'timestamp': nuevoMensaje.timestamp,
}; };
FirebaseFirestore.instance FirebaseFirestore.instance
.collection('chats') .collection('chats')
.doc(widget.eventoId) .doc(widget.eventoId)
.update({ .update({
'message': FieldValue.arrayUnion([nuevoMensajeMap]) 'message': FieldValue.arrayUnion([nuevoMensajeMap])
}); });
_textController.clear(); _textController.clear();
} else {}
}, },
child: Container( child: Container(
height: 50, height: 50,
@@ -227,7 +231,9 @@ class _ChatScreenState extends State<ChatScreen> {
Container( Container(
margin: const EdgeInsets.only(left: 60), margin: const EdgeInsets.only(left: 60),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16), vertical: 10,
horizontal: 16,
),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color(0xFFD5EFFF), color: Color(0xFFD5EFFF),
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
+210 -117
View File
@@ -34,6 +34,7 @@ class _CitaScreenState extends State<CitaScreen> {
Reference? ref_photo; Reference? ref_photo;
ScoresModel? scoresModel; ScoresModel? scoresModel;
bool? ver = true; bool? ver = true;
bool? pro;
String formatCurrency(int number) { String formatCurrency(int number) {
final formatter = final formatter =
@@ -77,12 +78,14 @@ class _CitaScreenState extends State<CitaScreen> {
ScoresModel.scoreTo(widget.evento.userId, false, false).then( ScoresModel.scoreTo(widget.evento.userId, false, false).then(
(ScoresModel s) => setState(() { (ScoresModel s) => setState(() {
scoresModel = s; scoresModel = s;
pro = true;
}), }),
); );
} else { } else {
ScoresModel.scoreTo(widget.evento.professionalId, true, false).then( ScoresModel.scoreTo(widget.evento.professionalId, true, false).then(
(ScoresModel s) => setState(() { (ScoresModel s) => setState(() {
scoresModel = s; scoresModel = s;
pro = false;
}), }),
); );
} }
@@ -239,130 +242,217 @@ class _CitaScreenState extends State<CitaScreen> {
style: const TextStyle( style: const TextStyle(
color: Colors.grey, fontStyle: FontStyle.italic), color: Colors.grey, fontStyle: FontStyle.italic),
), ),
const Padding( widget.evento.status == 'terminado'
padding: EdgeInsets.symmetric(vertical: 20), ? const SizedBox(height: 10)
child: Text( : const Padding(
'Medios de comunicación con el usuario.', padding: EdgeInsets.symmetric(vertical: 20),
style: TextStyle(color: Color(0xFF2BA4EC)), child: Text(
), 'Medios de comunicación con el usuario.',
), style: TextStyle(color: Color(0xFF2BA4EC)),
Row(
children: [
const Expanded(child: SizedBox()),
ElevatedButton(
onPressed: () => launch("tel:$numberPhone"),
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
), ),
), ),
child: const Padding( widget.evento.status == 'terminado'
padding: ? pro == true
EdgeInsets.symmetric(vertical: 18, horizontal: 0), ? widget.evento.professionalScored == true
child: Icon( ? const SizedBox()
Icons.phone_android, : Column(
size: 30, children: [
color: Color(0xFF2BA4EC), const SizedBox(height: 120),
), ElevatedButton(
), onPressed: () {
), Navigator.pushReplacement(
const SizedBox(width: 20), context,
ElevatedButton( CupertinoPageRoute(
onPressed: () async { builder: (BuildContext context) {
if (widget.evento.status != 'pendiente') { return ScoreScreen(
await FirebaseFirestore.instance evento: widget.evento,
.collection('chats') pro: pro!,
.doc(widget.evento.id) );
.set({ },
'professional_id': widget.evento.professionalId, ),
'user_id': widget.evento.userId, );
'message': [], },
}, SetOptions(merge: true)).catchError((error) => style: ElevatedButton.styleFrom(
print('Error al crear el documento: $error')); backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
Navigator.push( borderRadius: BorderRadius.circular(50),
context, ),
CupertinoPageRoute( elevation: 0,
builder: (BuildContext context) { minimumSize: const Size(230, 60),
return ChatScreen(eventoId: widget.evento.id); ),
}, child: const Text(
), 'Puntuar servicio',
); style: TextStyle(
} else { color: Colors.white,
final snackBar = SnackBar( fontWeight: FontWeight.bold,
backgroundColor: Colors.blue, fontSize: 18,
content: const Text( ),
'Primero acepta la solicitud', ),
style: TextStyle( ),
color: Colors.white, ],
fontWeight: FontWeight.bold, )
: widget.evento.userScored == true
? const SizedBox()
: Column(
children: [
const SizedBox(height: 120),
ElevatedButton(
onPressed: () {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Puntuar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
)
: Row(
children: [
const Expanded(child: SizedBox()),
ElevatedButton(
onPressed: () => launch("tel:$numberPhone"),
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
), ),
), ),
elevation: 8.0, child: const Padding(
shape: RoundedRectangleBorder( padding: EdgeInsets.symmetric(
borderRadius: BorderRadius.circular(10.0), vertical: 18, horizontal: 0),
child: Icon(
Icons.phone_android,
size: 30,
color: Color(0xFF2BA4EC),
),
), ),
); ),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () async {
if (widget.evento.status != 'pendiente') {
await FirebaseFirestore.instance
.collection('chats')
.doc(widget.evento.id)
.set(
{
'professional_id':
widget.evento.professionalId,
'user_id': widget.evento.userId,
'message': [],
},
SetOptions(
merge:
true)).catchError((error) => print(
'Error al crear el documento: $error'));
ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.push(
} context,
}, CupertinoPageRoute(
style: ElevatedButton.styleFrom( builder: (BuildContext context) {
foregroundColor: const Color(0xFF2BA4EC), return ChatScreen(
backgroundColor: Colors.white, eventoId: widget.evento.id);
shape: RoundedRectangleBorder( },
borderRadius: BorderRadius.circular(50), ),
side: const BorderSide( );
color: Color(0xFF2BA4EC), } else {
width: 2, final snackBar = SnackBar(
backgroundColor: Colors.blue,
content: const Text(
'Primero acepta la solicitud',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
Icons.message,
size: 30,
color: Color(0xFF2BA4EC),
),
),
), ),
), const SizedBox(width: 20),
), ElevatedButton(
child: const Padding( onPressed: () {
padding: _sendWhatsapp(numberPhone);
EdgeInsets.symmetric(vertical: 18, horizontal: 0), },
child: Icon( style: ElevatedButton.styleFrom(
Icons.message, foregroundColor: const Color(0xFF2BA4EC),
size: 30, backgroundColor: Colors.white,
color: Color(0xFF2BA4EC), shape: RoundedRectangleBorder(
), borderRadius: BorderRadius.circular(50),
), side: const BorderSide(
), color: Color(0xFF2BA4EC),
const SizedBox(width: 20), width: 2,
ElevatedButton( ),
onPressed: () { ),
_sendWhatsapp(numberPhone); ),
}, child: const Padding(
style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(
foregroundColor: const Color(0xFF2BA4EC), vertical: 18, horizontal: 0),
backgroundColor: Colors.white, child: Icon(
shape: RoundedRectangleBorder( CommunityMaterialIcons.whatsapp,
borderRadius: BorderRadius.circular(50), size: 30,
side: const BorderSide( color: Color(0xFF2BA4EC),
color: Color(0xFF2BA4EC), ),
width: 2, ),
), ),
), const Expanded(child: SizedBox()),
],
), ),
child: const Padding(
padding:
EdgeInsets.symmetric(vertical: 18, horizontal: 0),
child: Icon(
CommunityMaterialIcons.whatsapp,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
const Expanded(child: SizedBox()),
],
),
widget.evento.ubicacion == 'sitio' widget.evento.ubicacion == 'sitio'
? const SizedBox() ? const SizedBox()
: Padding( : Padding(
@@ -563,11 +653,14 @@ class _CitaScreenState extends State<CitaScreen> {
.collection("services") .collection("services")
.doc('${widget.evento.id}') .doc('${widget.evento.id}')
.update({"status": "terminado"}).then((value) { .update({"status": "terminado"}).then((value) {
Navigator.push( Navigator.pushReplacement(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (BuildContext context) { builder: (BuildContext context) {
return ScoreScreen(evento: widget.evento); return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
}, },
), ),
); );
+10
View File
@@ -54,6 +54,16 @@ class _LoginScreenState extends State<LoginScreen> {
completePhoneNumber = phoneNo.completeNumber; completePhoneNumber = phoneNo.completeNumber;
}, },
decoration: const InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)), borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
+39
View File
@@ -146,7 +146,23 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.only(bottom: 20),
child: TextFormField( child: TextFormField(
controller: controller.email, controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
}
final RegExp emailRegExp =
RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
}
return null;
},
decoration: const InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)), borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
@@ -157,6 +173,12 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(50), Radius.circular(50),
)), )),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Hello@gmail.com', hintText: 'Hello@gmail.com',
fillColor: Color.fromARGB(255, 239, 239, 239), fillColor: Color.fromARGB(255, 239, 239, 239),
filled: true, filled: true,
@@ -177,6 +199,12 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
child: TextFormField( child: TextFormField(
controller: controller.password, controller: controller.password,
obscureText: _obscureText, obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
}
return null;
},
decoration: InputDecoration( decoration: InputDecoration(
enabledBorder: const OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)), borderSide: BorderSide(color: Color(0xFFECECEC)),
@@ -188,6 +216,17 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(50), Radius.circular(50),
)), )),
border: const OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Contraseña', hintText: 'Contraseña',
fillColor: const Color.fromARGB(255, 239, 239, 239), fillColor: const Color.fromARGB(255, 239, 239, 239),
filled: true, filled: true,
+11
View File
@@ -69,6 +69,17 @@ class _NewNumberScreenState extends State<NewNumberScreen> {
completePhoneNumber = newPhoneNo.completeNumber; completePhoneNumber = newPhoneNo.completeNumber;
}, },
decoration: const InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)), borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
+41 -2
View File
@@ -450,7 +450,6 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String profession = _profession.toString(); String profession = _profession.toString();
double _space = 10; double _space = 10;
@@ -493,6 +492,12 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
TextFormField( TextFormField(
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
controller: _cedulaController, controller: _cedulaController,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Ingrese una cedula válida';
}
return null;
},
decoration: const InputDecoration( decoration: const InputDecoration(
prefixIcon: Icon(Icons.person_outline), prefixIcon: Icon(Icons.person_outline),
hintText: 'Cedula (Obligatorio)'), hintText: 'Cedula (Obligatorio)'),
@@ -638,13 +643,47 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
], ],
)), )),
), ),
Container(
margin: const EdgeInsets.only(
left: 40, right: 40, top: 40, bottom: 0),
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,
),
SizedBox(width: 15),
Text(
'Para añadir mas de una especialidad, \nenvie mas fotos y separe por comas (,).',
style: TextStyle(color: Colors.black, fontSize: 14),
),
],
),
),
Container( Container(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
margin: const EdgeInsets.only( margin: const EdgeInsets.only(
top: 80, right: 70, left: 70, bottom: 30), top: 80, right: 70, left: 70, bottom: 30),
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
sendInfo(); if (_formKey.currentState!.validate()) {
sendInfo();
}
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC), backgroundColor: const Color(0xFF2BA4EC),
+26 -2
View File
@@ -11,6 +11,7 @@ import 'package:prosappco/src/controllers/add_name_email_city.dart';
import 'package:prosappco/src/screens/city.dart'; import 'package:prosappco/src/screens/city.dart';
import 'package:prosappco/src/screens/new_number.dart'; import 'package:prosappco/src/screens/new_number.dart';
import 'package:prosappco/src/screens/new_password.dart'; import 'package:prosappco/src/screens/new_password.dart';
import 'package:prosappco/src/screens/service.dart';
import 'package:prosappco/src/services/select_image_profile.dart'; import 'package:prosappco/src/services/select_image_profile.dart';
import '../components/photo_view.dart'; import '../components/photo_view.dart';
@@ -385,11 +386,21 @@ class _ProfileScreenState extends State<ProfileScreen> {
children: [ children: [
TextFormField( TextFormField(
controller: _nameController, controller: _nameController,
maxLength: 50,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Porfavor ingrese un nombre.';
}
if (value.length < 5) {
return 'Debe tener al menos 5 caracteres.';
}
return null;
},
decoration: const InputDecoration( decoration: const InputDecoration(
prefixIcon: Icon(Icons.person_outline), prefixIcon: Icon(Icons.person_outline),
hintText: 'Nombre (Obligatorio)'), hintText: 'Nombre (Obligatorio)'),
), ),
const SizedBox(height: 20.0), const SizedBox(height: 0),
_email != null _email != null
? TextFormField( ? TextFormField(
onTap: () { onTap: () {
@@ -407,6 +418,17 @@ class _ProfileScreenState extends State<ProfileScreen> {
) )
: TextFormField( : TextFormField(
controller: _emailController, controller: _emailController,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
}
final RegExp emailRegExp = RegExp(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
}
return null;
},
decoration: const InputDecoration( decoration: const InputDecoration(
prefixIcon: Icon(Icons.email_outlined), prefixIcon: Icon(Icons.email_outlined),
hintText: 'Email (Obligatorio)'), hintText: 'Email (Obligatorio)'),
@@ -490,7 +512,9 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: const EdgeInsets.only(bottom: 30), padding: const EdgeInsets.only(bottom: 30),
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
await updateInfo(); if (_formKey.currentState!.validate()) {
await updateInfo();
}
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC), backgroundColor: const Color(0xFF2BA4EC),
+45
View File
@@ -156,7 +156,24 @@ class _RegisterScreenState extends State<RegisterScreen> {
padding: const EdgeInsets.only(bottom: 18), padding: const EdgeInsets.only(bottom: 18),
child: TextFormField( child: TextFormField(
controller: controller.email, controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
}
final RegExp emailRegExp =
RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
}
return null;
},
decoration: const InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: borderSide:
BorderSide(color: Color(0xFFECECEC)), BorderSide(color: Color(0xFFECECEC)),
@@ -169,6 +186,13 @@ class _RegisterScreenState extends State<RegisterScreen> {
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(50), Radius.circular(50),
)), )),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Hello@gmail.com', hintText: 'Hello@gmail.com',
fillColor: Color.fromARGB(255, 239, 239, 239), fillColor: Color.fromARGB(255, 239, 239, 239),
filled: true, filled: true,
@@ -189,13 +213,34 @@ class _RegisterScreenState extends State<RegisterScreen> {
child: TextFormField( child: TextFormField(
controller: controller.password, controller: controller.password,
obscureText: _obscureText, obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
}
if (value.length <= 6) {
return 'Contraseña muy corta';
}
return null;
},
decoration: InputDecoration( decoration: InputDecoration(
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: const OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: borderSide:
BorderSide(color: Color(0xFFECECEC)), BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(50), Radius.circular(50),
)), )),
border: const OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: const OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide:
BorderSide(color: Color(0xFFECECEC)), BorderSide(color: Color(0xFFECECEC)),
+1 -1
View File
@@ -23,7 +23,7 @@ class _ReputationProScreenState extends State<ReputationProScreen> {
final uid = AuthenticationRepository.instance.getCurrentUserUid(); final uid = AuthenticationRepository.instance.getCurrentUserUid();
if (scoresModel == null) { if (scoresModel == null) {
ScoresModel.scoreFrom(uid.toString(), false, true).then( ScoresModel.scoreTo(uid.toString(), true, true).then(
(ScoresModel s) => setState(() => scoresModel = s), (ScoresModel s) => setState(() => scoresModel = s),
); );
} }
+36 -22
View File
@@ -10,8 +10,8 @@ import 'package:prosappco/src/models/user_model.dart';
class ScoreScreen extends StatefulWidget { class ScoreScreen extends StatefulWidget {
final Event evento; final Event evento;
final bool pro;
const ScoreScreen({super.key, required this.evento}); const ScoreScreen({super.key, required this.evento, required this.pro});
@override @override
State<ScoreScreen> createState() => ScoreScreenState(); State<ScoreScreen> createState() => ScoreScreenState();
@@ -46,7 +46,11 @@ class ScoreScreenState extends State<ScoreScreen> {
} }
return Scaffold( return Scaffold(
appBar: PopAppbar(onPressed: () {}, label: 'Puntuación'), appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Puntuación'),
body: Column( body: Column(
children: [ children: [
Padding( Padding(
@@ -105,8 +109,8 @@ class ScoreScreenState extends State<ScoreScreen> {
), ),
TextFormField( TextFormField(
maxLines: null, maxLines: null,
keyboardType: TextInputType maxLength: 250,
.multiline, // establece el tipo de teclado como "multilinea" keyboardType: TextInputType.multiline,
controller: commentController, controller: commentController,
), ),
], ],
@@ -119,25 +123,35 @@ class ScoreScreenState extends State<ScoreScreen> {
children: [ children: [
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
if (widget.evento.professionalId == uid) { if (widget.pro) {
FirebaseFirestore.instance.collection("scores").add({ FirebaseFirestore.instance
"comment": commentController.text, .collection("services")
"from_user": uid, .doc(widget.evento.id)
"is_from_professional": false, .update({'professional_scored': true}).then((value) {
"score": _rating, FirebaseFirestore.instance.collection("scores").add({
"to_user": widget.evento.userId, "comment": commentController.text,
}).then((value) { "from_user": widget.evento.professionalId,
// Navigator.pushReplacementNamed(context, '/score'); "is_from_professional": false,
"score": _rating,
"to_user": widget.evento.userId,
}).then((value) {
Navigator.pop(context);
});
}); });
} else { } else {
FirebaseFirestore.instance.collection("scores").add({ FirebaseFirestore.instance
"comment": commentController.text, .collection("services")
"from_user": uid, .doc(widget.evento.id)
"is_from_professional": true, .update({'user_scored': true}).then((value) {
"score": _rating, FirebaseFirestore.instance.collection("scores").add({
"to_user": widget.evento.professionalId, "comment": commentController.text,
}).then((value) { "from_user": widget.evento.userId,
// Navigator.pushReplacementNamed(context, '/score'); "is_from_professional": true,
"score": _rating,
"to_user": widget.evento.professionalId,
}).then((value) {
Navigator.pop(context);
});
}); });
} }
}, },
+77 -43
View File
@@ -15,6 +15,7 @@ import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/user_model.dart'; import 'package:prosappco/src/models/user_model.dart';
import 'package:prosappco/src/screens/professional.dart'; import 'package:prosappco/src/screens/professional.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/service_after.dart'; import 'package:prosappco/src/screens/service_after.dart';
import 'package:prosappco/src/screens/service_type.dart'; import 'package:prosappco/src/screens/service_type.dart';
@@ -108,6 +109,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
final TimeOfDay? pickedTime = await showTimePicker( final TimeOfDay? pickedTime = await showTimePicker(
context: context, context: context,
initialTime: TimeOfDay.now(), initialTime: TimeOfDay.now(),
// builder: (context, child) { // builder: (context, child) {
// return Theme(data: ThemeData.dark(), child: child!); // return Theme(data: ThemeData.dark(), child: child!);
// }, // },
@@ -131,6 +133,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
final FirebaseStorage storage = FirebaseStorage.instance; final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid(); final uid = AuthenticationRepository.instance.getCurrentUserUid();
UserModel? user;
final fcmToken = FirebaseMessaging.instance.getToken(); final fcmToken = FirebaseMessaging.instance.getToken();
BitmapDescriptor? _markerIcon; BitmapDescriptor? _markerIcon;
@@ -150,6 +153,12 @@ class _ServiceScreenState extends State<ServiceScreen> {
} }
}); });
if (user == null) {
UserModel.getUser(uid.toString()).then(
(UserModel s) => setState(() => user = s),
);
}
BitmapDescriptor.fromAssetImage( BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)), const ImageConfiguration(size: Size(48, 48)),
'images/pro_marke.png') 'images/pro_marke.png')
@@ -566,51 +575,76 @@ class _ServiceScreenState extends State<ServiceScreen> {
const SizedBox(height: 20), const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
final DateTime combinedDate = DateTime( if (user?.name == null ||
_selectedDate!.year, user?.name == '' ||
_selectedDate!.month, user?.phoneNumber == null ||
_selectedDate!.day, user?.phoneNumber == '') {
_selectedTime!.hour, Navigator.push(
_selectedTime!.minute); context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ProfileScreen();
},
),
);
} else {
try {
final DateTime combinedDate = DateTime(
_selectedDate!.year,
_selectedDate!.month,
_selectedDate!.day,
_selectedTime!.hour,
_selectedTime!.minute);
DateTime time2 = DateTime time2 = combinedDate
combinedDate.add(const Duration(hours: 2)); .add(const Duration(hours: 2));
UserModel.getUser(uid.toString()).then((value) { UserModel.getUser(uid.toString())
eventoService .then((value) {
.createEvent( eventoService
value.name, .createEvent(
_observacionController.text, value.name,
'$_selectedDate', _observacionController.text,
'$combinedDate', '$_selectedDate',
'$time2', '$combinedDate',
professionalId, '$time2',
ubicacion, professionalId,
_locationController.text, ubicacion,
ubicacion != 'sitio' _locationController.text,
? coordinates.latitude ubicacion != 'sitio'
: professionalLatitude, ? coordinates.latitude
ubicacion != 'sitio' : professionalLatitude,
? coordinates.longitude ubicacion != 'sitio'
: professionalLongitude, ? coordinates.longitude
'pendiente', : professionalLongitude,
professionalTarifa == 0 'pendiente',
? 0 professionalTarifa == 0
: professionalTarifa, ? 0
) : professionalTarifa,
.then((value) { false,
Navigator.pushReplacement( false,
context, )
CupertinoPageRoute( .then((value) {
builder: (BuildContext context) { Navigator.pushReplacement(
return ServiceAfterScreen( context,
eventoId: value, CupertinoPageRoute(
); builder: (BuildContext context) {
}, return ServiceAfterScreen(
), eventoId: value,
);
},
),
);
});
});
} catch (e) {
Get.snackbar(
'Llena todos los campos',
'Asegurate de llenar todos los campos.',
snackPosition: SnackPosition.BOTTOM,
); );
}); }
}); }
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC), backgroundColor: const Color(0xFF2BA4EC),
@@ -629,7 +663,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
), ),
), ),
), ),
Text('${_selectedDate?.weekday}') Text('${_selectedDate?.weekday}'),
], ],
), ),
), ),