feat: migrate prosappco from Firebase to NestJS REST API (Fase 2)

- Replace all Firebase* repositories with Api* repositories using HTTP + SharedPreferences JWT
- Remove Firebase.initializeApp() and firebase_messaging background handler from main.dart
- Update DI (app_di.dart) to inject Api* repositories instead of Firebase* ones
- Replace all Timestamp/cloud_firestore usage with ISO 8601 String dates
- Stub PhoneVerificationService (Firebase phone OTP → backend OTP when implemented)
- Add ApiService singleton with JWT management in lib/services/
- Legacy firebase_*_repository.dart files preserved for Fase 4 cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-17 16:53:11 -05:00
co-authored by Claude Sonnet 4.6
parent 726cf12fd2
commit 733384091c
65 changed files with 1404 additions and 389 deletions
@@ -0,0 +1,76 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:city_repository/city_repository.dart';
import 'city_repo.dart';
const _base = 'https://backend.prosapp.co/api/v1';
class ApiCityRepository implements CityRepository {
List<CityUi>? _cities;
Future<String?> _getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('token');
}
Future<Map<String, String>> _headers() async {
final t = await _getToken();
return {
'Content-Type': 'application/json',
if (t != null) 'Authorization': 'Bearer $t',
};
}
@override
Future<List<CityUi>> getCities() async {
if (_cities != null) return _cities!;
final List<CityUi> cities = [];
try {
final countriesRes = await http.get(
Uri.parse('$_base/locations/countries'),
headers: await _headers(),
);
final countries = jsonDecode(countriesRes.body) as List;
for (final country in countries) {
final countryId = country['id']?.toString() ?? '';
final countryName = country['name']?.toString() ?? '';
final regionsRes = await http.get(
Uri.parse('$_base/locations/countries/$countryId/regions'),
headers: await _headers(),
);
final regions = jsonDecode(regionsRes.body) as List;
for (final region in regions) {
final regionId = region['id']?.toString() ?? '';
final regionName = region['name']?.toString() ?? '';
final citiesRes = await http.get(
Uri.parse('$_base/locations/regions/$regionId/cities'),
headers: await _headers(),
);
final citiesList = jsonDecode(citiesRes.body) as List;
for (final city in citiesList) {
cities.add(CityUi(
cityName: city['name']?.toString() ?? '',
coordsOfCity: city['coords']?.toString() ?? '',
stateOfCity: regionName,
countryOfCity: countryName,
));
}
}
}
} catch (e) {
rethrow;
}
_cities = cities;
return cities;
}
}