fix: align Flutter API repositories with backend routes (Fase 3 prep)

- api_service_repository: use correct endpoints (/services/me, /services/professional,
  /professional/requests, /professional/history, /professional/calendar)
  instead of broken /services?userId= query params
- api_service_repository: fix status string↔enum, createService DTO,
  paginated {data,meta} response, stub setProfessionalScored/setUserScored,
  extract professionalId from nested professionals.user_id
- api_score_repository: fix /comments/user/:id and /comments/professional/:id
- api_professional_repository + api_user_repository: handle paginated response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-17 18:29:47 -05:00
co-authored by Claude Sonnet 4.6
parent af91e0ba04
commit 7fb0615169
4 changed files with 117 additions and 115 deletions
@@ -7,25 +7,18 @@ import 'package:score_repository/src/entities/comment_entity.dart';
const _base = 'https://backend.prosapp.co/api/v1';
/// API-backed replacement for FirebaseScoreRepository.
/// Mirrors the same public API so existing blocs work without changes.
class ApiScoreRepository {
ReputationEntity? _reputation;
final StreamController<ReputationEntity> _reputationController =
StreamController<ReputationEntity>.broadcast();
final _reputationController = StreamController<ReputationEntity>.broadcast();
String? _token;
ApiScoreRepository() {
_reputationController.add(_emptyReputation());
_reputationController.add(_empty());
}
ReputationEntity _emptyReputation() => const ReputationEntity(
total: 0,
average: 0,
totalPro: 0,
averagePro: 0,
);
ReputationEntity _empty() =>
const ReputationEntity(total: 0, average: 0, totalPro: 0, averagePro: 0);
Future<String?> _getToken() async {
if (_token != null) return _token;
@@ -43,7 +36,7 @@ class ApiScoreRepository {
Stream<ReputationEntity> streamReputation() => _reputationController.stream;
ReputationEntity getReputation() => _reputation ?? _emptyReputation();
ReputationEntity getReputation() => _reputation ?? _empty();
Future<ReputationEntity> getReputationByUserId(String userId) async {
try {
@@ -57,51 +50,33 @@ class ApiScoreRepository {
_reputationController.add(rep);
return rep;
} catch (_) {
return _emptyReputation();
return _empty();
}
}
Stream<List<CommentEntity>> getScoresForUser(String userId) {
Stream<List<CommentEntity>> getScoresForUser(String userId) =>
_streamComments('/comments/user/$userId');
Stream<List<CommentEntity>> getScoresForProfessional(String userId) =>
_streamComments('/comments/professional/$userId');
Stream<List<CommentEntity>> _streamComments(String path) {
final controller = StreamController<List<CommentEntity>>();
_fetchComments(userId: userId, isFromUser: false).then((list) {
_fetchComments(path).then((list) {
controller.add(list);
controller.close();
}).catchError((e) {
}).catchError((_) {
controller.add([]);
controller.close();
});
return controller.stream;
}
Stream<List<CommentEntity>> getScoresForProfessional(String userId) {
final controller = StreamController<List<CommentEntity>>();
_fetchComments(userId: userId, isFromUser: true).then((list) {
controller.add(list);
controller.close();
}).catchError((e) {
controller.add([]);
controller.close();
});
return controller.stream;
}
Future<List<CommentEntity>> _fetchComments({
required String userId,
required bool isFromUser,
}) async {
try {
final res = await http.get(
Uri.parse('$_base/comments/reputation/$userId'),
headers: await _headers(),
);
final data = jsonDecode(res.body);
if (data is! Map) return [];
// The endpoint returns reputation summary, not individual comments
// Return empty list since we only have aggregated data
return [];
} catch (_) {
return [];
}
Future<List<CommentEntity>> _fetchComments(String path) async {
final res = await http.get(Uri.parse('$_base$path'), headers: await _headers());
final body = jsonDecode(res.body);
final List raw = body is Map ? (body['data'] as List? ?? []) : (body as List? ?? []);
return raw.map((e) => CommentEntity.fromDocument(e as Map<String, dynamic>)).toList();
}
Future<void> addComment(CommentEntity comment) async {
@@ -110,7 +85,6 @@ class ApiScoreRepository {
Uri.parse('$_base/comments'),
headers: await _headers(),
body: jsonEncode({
'author_id': comment.authorId,
'destination_id': comment.destinationId,
'service_id': comment.serviceId,
'content': comment.content,
@@ -118,10 +92,7 @@ class ApiScoreRepository {
'is_from_user': comment.isFromUser,
}),
);
// Refresh reputation after adding a comment
await getReputationByUserId(comment.destinationId);
} catch (_) {
// Stub — do not crash if comment fails
}
} catch (_) {}
}
}