feat: implement Ratings API (Phase 2.5)

Complete the match lifecycle with partner rating functionality.

Backend changes:
- Add POST /api/matches/:slug/ratings endpoint to create ratings
  * Validate score range (1-5)
  * Prevent duplicate ratings (unique constraint per match+rater+rated)
  * Auto-complete match when both users have rated
  * Return detailed rating data with user and event info
- Add GET /api/users/:username/ratings endpoint to fetch user ratings
  * Calculate and return average rating
  * Include rater details and event context for each rating
  * Limit to last 50 ratings
- Add hasRated field to GET /api/matches/:slug response
  * Check if current user has already rated the match
  * Enable frontend to prevent duplicate rating attempts

Frontend changes:
- Update RatePartnerPage to use real API instead of mocks
  * Load match data and partner info
  * Submit ratings with score, comment, and wouldCollaborateAgain
  * Check hasRated flag and redirect if already rated
  * Validate match status before allowing rating
  * Show loading state and proper error handling
- Update MatchChatPage to show rating status
  * Replace "Rate Partner" button with "✓ Rated" badge when user has rated
  * Improve button text from "End & rate" to "Rate Partner"
- Add ratings API functions
  * matchesAPI.createRating(slug, ratingData)
  * ratingsAPI.getUserRatings(username)

User flow:
1. After match is accepted, users can rate each other
2. Click "Rate Partner" in chat to navigate to rating page
3. Submit 1-5 star rating with optional comment
4. Rating saved and user redirected to matches list
5. Chat shows "✓ Rated" badge instead of rating button
6. Match marked as 'completed' when both users have rated
7. Users cannot rate the same match twice
This commit is contained in:
Radosław Gierwiało
2025-11-14 22:35:32 +01:00
parent c2010246e3
commit 49e492a8f8
5 changed files with 338 additions and 19 deletions

View File

@@ -309,6 +309,22 @@ export const matchesAPI = {
const data = await fetchAPI(`/matches/${matchSlug}/messages`);
return data;
},
async createRating(matchSlug, { score, comment, wouldCollaborateAgain }) {
const data = await fetchAPI(`/matches/${matchSlug}/ratings`, {
method: 'POST',
body: JSON.stringify({ score, comment, wouldCollaborateAgain }),
});
return data;
},
};
// Ratings API
export const ratingsAPI = {
async getUserRatings(username) {
const data = await fetchAPI(`/users/${username}/ratings`);
return data;
},
};
export { ApiError };