feat: migrate Firebase → NestJS REST API backend

Replace all Firebase SDK (Auth, Firestore, Storage) with HTTP calls
to backend.prosapp.co/api/v1:

- New ApiService singleton (JWT token, GET/POST/PATCH/DELETE/upload)
- auth_provider: Firebase Auth → /auth/login, /auth/register, /auth/phone/*
- services_provider + calendar_services_provider → /services endpoints
- professional_provider + professionals_provider → /professional-info, /users/professionals
- profile_form_provider + professional_form_provider → /users/me, /storage/upload
- cities/professions/settings providers → /cities, /professions, /settings
- firebase_chat_repository → polling via /chats endpoints (3s interval)
- firebase_score_repository → polling via /comments endpoints (10s interval)
- professional_detail_provider → /users/:id + /professional-info/:id
- dashboard_view: Firestore.add → POST /services
- chat_view + rating_view: FirebaseAuth.uid → AuthProvider.user.id
- Models: Timestamp → String for createdAt fields
- google_fonts upgraded to ^8.1.0 (Dart 3.12 compat)
- Remove firebase_core, firebase_auth, cloud_firestore, firebase_storage from pubspec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-18 15:53:39 -05:00
co-authored by Claude Sonnet 4.6
parent 15175c1b91
commit 60216fd0e3
29 changed files with 440 additions and 1304 deletions
+20 -96
View File
@@ -1,18 +1,14 @@
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:prosapp_web_app/models/pro_state.dart';
import 'package:prosapp_web_app/models/usuario.dart';
import 'package:prosapp_web_app/services/api_service.dart';
import 'package:prosapp_web_app/services/notifications_service.dart';
class ProfileFormProvider extends ChangeNotifier {
Usuario? user;
GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? _verificationId;
final _api = ApiService.instance;
void copyUserWith({
String? id,
@@ -43,117 +39,45 @@ class ProfileFormProvider extends ChangeNotifier {
notifyListeners();
}
bool _validForm() {
return formKey.currentState!.validate();
}
bool _validForm() => formKey.currentState!.validate();
Future<void> updateUserInfo() async {
if (!_validForm()) return;
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user!.id)
.withConverter(
fromFirestore: (snapshot, _) =>
Usuario.fromDocument(snapshot.data()!),
toFirestore: (user, _) => user.toDocument(),
);
await docUser.set(user!);
await _api.patch('/users/me', user!.toDocument());
NotificationsService.showSnackbar('Información actualizada');
}
Future<void> updateUserInfoNoValid() async {
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user!.id)
.withConverter(
fromFirestore: (snapshot, _) =>
Usuario.fromDocument(snapshot.data()!),
toFirestore: (user, _) => user.toDocument(),
);
await docUser.set(user!);
await _api.patch('/users/me', user!.toDocument());
NotificationsService.showSnackbar('Información actualizada');
}
Future<Usuario> uploadPicture(Uint8List bytes) async {
try {
final storageRef = FirebaseStorage.instance
.ref()
.child('${user!.id}/PP/${user!.id}_lead');
await storageRef.putData(bytes);
final url = await storageRef.getDownloadURL();
copyUserWith(picture: url);
notifyListeners();
return user!;
} catch (e) {
print("Error al subir la imagen: $e");
rethrow;
}
final url = await _api.upload(bytes, 'profile_${user!.id}.jpg');
if (url != null) copyUserWith(picture: url);
notifyListeners();
return user!;
}
// Agregar numero
Future<void> signUpWithPhoneNumber(String phoneNumber) async {
try {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async {
await FirebaseAuth.instance.signInWithCredential(credential);
NotificationsService.showSnackbar('Autenticación exitosa');
},
verificationFailed: (FirebaseAuthException e) {
NotificationsService.showSnackbar(
'Error en la verificación: ${e.message}');
},
codeSent: (String verificationId, int? resendToken) {
_verificationId = verificationId;
NotificationsService.showSnackbar(
'Código enviado al número $phoneNumber');
},
codeAutoRetrievalTimeout: (String verificationId) {
_verificationId = verificationId;
},
);
NotificationsService.showSnackbar('Código enviado');
await _api.post('/auth/phone/link/send', {'phone': phoneNumber});
NotificationsService.showSnackbar('Código enviado al número $phoneNumber');
} catch (e) {
NotificationsService.showSnackbar('Error al registrar con teléfono: $e');
NotificationsService.showSnackbar('Error al enviar código: $e');
}
}
Future<bool> linkPhoneNumberToExistingAccount(
String phoneNumber, String code) async {
Future<bool> linkPhoneNumberToExistingAccount(String phoneNumber, String code) async {
try {
var phoneAuthCredential = PhoneAuthProvider.credential(
verificationId: _verificationId!,
smsCode: code,
);
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
await user.linkWithCredential(phoneAuthCredential);
copyUserWith(phone: phoneNumber);
notifyListeners();
NotificationsService.showSnackbar('Número vinculado exitosamente');
return true;
}
return false;
await _api.post('/auth/phone/link/verify', {'phone': phoneNumber, 'code': code});
copyUserWith(phone: phoneNumber);
notifyListeners();
NotificationsService.showSnackbar('Número vinculado exitosamente');
return true;
} catch (e) {
if (e is FirebaseAuthException && e.code == 'invalid-verification-code') {
NotificationsService.showSnackbar('Código de verificación inválido');
return false;
} else {
NotificationsService.showSnackbar('Error al vincular número: $e');
rethrow;
}
NotificationsService.showSnackbar('Código de verificación inválido');
return false;
}
}
}