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:
co-authored by
Claude Sonnet 4.6
parent
af91e0ba04
commit
7fb0615169
@@ -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 (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user