feat: migrate prosapp_web_app from Firebase to NestJS API
- Replace Firebase Auth with JWT stored in SharedPreferences - Replace Firestore with REST API calls via new ApiService - Replace Firebase Storage with POST /storage/upload - Remove firebase_auth, firebase_core, cloud_firestore, firebase_storage, firebase_messaging, google_sign_in deps - Add api_service.dart as central HTTP client Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
1c20d0c0cd
commit
b95e0630de
@@ -1,6 +1,3 @@
|
||||
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';
|
||||
@@ -12,13 +9,11 @@ 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/setting_model.dart';
|
||||
import 'package:prosappco/src/models/user_model.dart';
|
||||
import 'package:prosappco/src/presentation/screens/chat.dart';
|
||||
import 'package:prosappco/src/presentation/screens/score.dart';
|
||||
import 'package:prosappco/src/services/api_service.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:community_material_icon/community_material_icon.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
class CitaScreen extends StatefulWidget {
|
||||
final Event evento;
|
||||
@@ -30,18 +25,13 @@ class CitaScreen extends StatefulWidget {
|
||||
|
||||
class _CitaScreenState extends State<CitaScreen> {
|
||||
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
||||
UserModel? user;
|
||||
DateTime today = DateTime.now();
|
||||
String nombre = '';
|
||||
String userToken = '';
|
||||
String numberPhone = '';
|
||||
int tarifa = 0;
|
||||
Reference? ref_photo;
|
||||
String? photoUrl;
|
||||
ScoresModel? scoresModel;
|
||||
bool? ver = true;
|
||||
bool? pro;
|
||||
String proName = '';
|
||||
late final FirebaseAuth _auth;
|
||||
|
||||
String formatCurrency(int number) {
|
||||
final formatter =
|
||||
@@ -49,48 +39,11 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
return '\$${formatter.format(number)}';
|
||||
}
|
||||
|
||||
Future<void> sendPushNotification(
|
||||
String user, String accion, String proName) async {
|
||||
try {
|
||||
http.Response response = await http.post(
|
||||
Uri.parse('https://fcm.googleapis.com/fcm/send'),
|
||||
headers: <String, String>{
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Authorization':
|
||||
'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2',
|
||||
},
|
||||
body: jsonEncode(
|
||||
<String, dynamic>{
|
||||
'notification': <String, dynamic>{
|
||||
'body': accion == 'rechazo'
|
||||
? '$proName a rechazado tu solicitud de servicio'
|
||||
: '$proName a aprobado tu solicitud de servicio',
|
||||
'title': '$proName $accion',
|
||||
},
|
||||
'priority': 'high',
|
||||
'data': <String, dynamic>{
|
||||
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
|
||||
'id': '1',
|
||||
'status': 'done',
|
||||
'screen': 'misservicios',
|
||||
},
|
||||
'to': user
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
response;
|
||||
} catch (e) {
|
||||
print('error al enviar notificacion $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openMap(double lat, double lng) async {
|
||||
final Uri _url =
|
||||
final Uri url =
|
||||
Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng');
|
||||
|
||||
if (!await launchUrl(_url)) {
|
||||
throw Exception('Could not launch $_url');
|
||||
if (!await launchUrl(url)) {
|
||||
throw Exception('Could not launch $url');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,23 +56,14 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
}
|
||||
|
||||
SettingModel? settings;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
||||
_auth = FirebaseAuth.instance;
|
||||
|
||||
final currentUser = _auth.currentUser;
|
||||
if (currentUser != null && currentUser.displayName != null) {
|
||||
proName = currentUser.displayName!;
|
||||
}
|
||||
|
||||
if (settings == null) {
|
||||
SettingModel.getSettings().then(
|
||||
(SettingModel value) => setState(() {
|
||||
settings = value;
|
||||
}),
|
||||
(SettingModel value) => setState(() => settings = value),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,36 +84,42 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_loadOtherUser();
|
||||
}
|
||||
|
||||
Future<void> _loadOtherUser() async {
|
||||
final targetId = (uid != widget.evento.userId)
|
||||
? widget.evento.userId
|
||||
: widget.evento.professionalId;
|
||||
try {
|
||||
final Map<String, dynamic> data =
|
||||
await ApiService.instance.get('/users/$targetId');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
nombre = data['name'] ?? '';
|
||||
photoUrl = data['picture'];
|
||||
numberPhone = data['phone'] ?? '';
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading user: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateStatus(String status) async {
|
||||
try {
|
||||
await ApiService.instance
|
||||
.patch('/services/${widget.evento.id}', {'status': status});
|
||||
} catch (e) {
|
||||
print('Error updating service status: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
today.difference(DateTime.parse(widget.evento.range1Hour1));
|
||||
final eventDate = DateFormat('yyyy-MM-dd').parse(widget.evento.day);
|
||||
|
||||
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;
|
||||
userToken = value.token ?? '';
|
||||
numberPhone = value.phoneNumber ?? '';
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
UserModel.getUser(widget.evento.professionalId).then((value) {
|
||||
setState(() {
|
||||
nombre = value.name;
|
||||
ref_photo = value.photo;
|
||||
numberPhone = value.phoneNumber ?? '';
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: PopAppbar(
|
||||
onPressed: () {
|
||||
@@ -183,7 +133,7 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
children: [
|
||||
ListTile(
|
||||
leading: ReferencePhoto(
|
||||
ref: ref_photo,
|
||||
ref: photoUrl,
|
||||
size: 50,
|
||||
sizeCircle: 50,
|
||||
sizeIcon: 35,
|
||||
@@ -287,7 +237,6 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
)
|
||||
: const SizedBox(),
|
||||
const SizedBox(height: 15),
|
||||
// Text('${widget.evento.range1Hour1} - ${DateTime.now()}'),
|
||||
Text(
|
||||
textAlign: TextAlign.center,
|
||||
'"${widget.evento.description?.trim()}"',
|
||||
@@ -422,28 +371,12 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (widget.evento.status != 'pendiente') {
|
||||
final chatDoc = FirebaseFirestore
|
||||
.instance
|
||||
.collection('chats')
|
||||
.doc(widget.evento.id);
|
||||
final chatSnapshot =
|
||||
await chatDoc.get();
|
||||
|
||||
if (!chatSnapshot.exists ||
|
||||
chatSnapshot.data()!['message'] ==
|
||||
null) {
|
||||
await chatDoc.set(
|
||||
{
|
||||
'professional_id':
|
||||
widget.evento.professionalId,
|
||||
'user_id': widget.evento.userId,
|
||||
'message': [],
|
||||
},
|
||||
SetOptions(merge: true),
|
||||
).catchError((error) => print(
|
||||
'Error al crear el documento: $error'));
|
||||
}
|
||||
|
||||
// Start or retrieve chat via API
|
||||
try {
|
||||
await ApiService.instance.post(
|
||||
'/chat/start/${widget.evento.professionalId}',
|
||||
{});
|
||||
} catch (_) {}
|
||||
Navigator.push(
|
||||
context,
|
||||
CupertinoPageRoute(
|
||||
@@ -453,35 +386,7 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// if (widget.evento.status != 'pendiente') {
|
||||
// await FirebaseFirestore.instance
|
||||
// .collection('chats')
|
||||
// .doc(widget.evento.id)
|
||||
// .set(
|
||||
// {
|
||||
// 'professional_id':
|
||||
// widget.evento.professionalId,
|
||||
// 'user_id': widget.evento.userId,
|
||||
// 'message': [],
|
||||
// },
|
||||
// SetOptions(
|
||||
// merge:
|
||||
// true)).catchError((error) => print(
|
||||
// 'Error al crear el documento: $error'));
|
||||
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// CupertinoPageRoute(
|
||||
// builder: (BuildContext context) {
|
||||
// return ChatScreen(
|
||||
// eventoId: widget.evento.id);
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
else {
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'El profesional aun no ha aceptado tu solicitud',
|
||||
'Debes esperar a que el profesional acepte tu solicitud para poder iniciar un chat.',
|
||||
@@ -539,7 +444,7 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
const Expanded(child: SizedBox()),
|
||||
],
|
||||
)
|
||||
: SizedBox(),
|
||||
: const SizedBox(),
|
||||
widget.evento.ubicacion == 'sitio'
|
||||
? const SizedBox()
|
||||
: Padding(
|
||||
@@ -601,16 +506,9 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
const Duration(minutes: 30) &&
|
||||
ver == true)
|
||||
? ElevatedButton(
|
||||
onPressed: () {
|
||||
FirebaseFirestore.instance
|
||||
.collection("services")
|
||||
.doc('${widget.evento.id}')
|
||||
.update({"status": "iniciado"}).then(
|
||||
(value) {
|
||||
setState(() {
|
||||
ver = false;
|
||||
});
|
||||
});
|
||||
onPressed: () async {
|
||||
await _updateStatus('iniciado');
|
||||
setState(() => ver = false);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2BA4EC),
|
||||
@@ -634,19 +532,10 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
),
|
||||
widget.evento.professionalId == uid
|
||||
? ElevatedButton(
|
||||
onPressed: () {
|
||||
FirebaseFirestore.instance
|
||||
.collection("services")
|
||||
.doc('${widget.evento.id}')
|
||||
.update({"status": "denegado"}).then(
|
||||
(value) {
|
||||
if (userToken != '') {
|
||||
sendPushNotification(
|
||||
userToken, 'rechazo', proName);
|
||||
}
|
||||
Navigator.pushReplacementNamed(
|
||||
context, '/solicitud');
|
||||
});
|
||||
onPressed: () async {
|
||||
await _updateStatus('denegado');
|
||||
Navigator.pushReplacementNamed(
|
||||
context, '/solicitud');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFEC2B2B),
|
||||
@@ -679,19 +568,10 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: widget.evento.professionalId == uid
|
||||
? ElevatedButton(
|
||||
onPressed: () {
|
||||
FirebaseFirestore.instance
|
||||
.collection("services")
|
||||
.doc('${widget.evento.id}')
|
||||
.update({"status": "aprobado"}).then(
|
||||
(value) {
|
||||
if (userToken != '') {
|
||||
sendPushNotification(
|
||||
userToken, 'acepto', proName);
|
||||
}
|
||||
Navigator.pushReplacementNamed(
|
||||
context, '/solicitud');
|
||||
});
|
||||
onPressed: () async {
|
||||
await _updateStatus('aprobado');
|
||||
Navigator.pushReplacementNamed(
|
||||
context, '/solicitud');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2BA4EC),
|
||||
@@ -713,18 +593,9 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
: const SizedBox(),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
FirebaseFirestore.instance
|
||||
.collection("services")
|
||||
.doc('${widget.evento.id}')
|
||||
.update({"status": "denegado"}).then((value) {
|
||||
if (userToken != '') {
|
||||
sendPushNotification(
|
||||
userToken, 'rechazo', proName);
|
||||
}
|
||||
Navigator.pushReplacementNamed(
|
||||
context, '/solicitud');
|
||||
});
|
||||
onPressed: () async {
|
||||
await _updateStatus('denegado');
|
||||
Navigator.pushReplacementNamed(context, '/solicitud');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFEC2B2B),
|
||||
@@ -753,23 +624,19 @@ class _CitaScreenState extends State<CitaScreen> {
|
||||
child: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
FirebaseFirestore.instance
|
||||
.collection("services")
|
||||
.doc('${widget.evento.id}')
|
||||
.update({"status": "terminado"}).then((value) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
CupertinoPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return ScoreScreen(
|
||||
evento: widget.evento,
|
||||
pro: pro!,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
onPressed: () async {
|
||||
await _updateStatus('terminado');
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
CupertinoPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return ScoreScreen(
|
||||
evento: widget.evento,
|
||||
pro: pro!,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2BA4EC),
|
||||
|
||||
Reference in New Issue
Block a user