diff --git a/backend/routers/reviews.py b/backend/routers/reviews.py index 64c420c..09b7199 100644 --- a/backend/routers/reviews.py +++ b/backend/routers/reviews.py @@ -2,7 +2,7 @@ from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlmodel import Session, select, func from database import get_session -from models import Review, User +from models import Review, User, Profile from schemas import ReviewCreate, ReviewRead from auth import get_current_user from services.gamification import award_xp, check_and_award_badges, update_streak, XP_REWARDS @@ -43,7 +43,7 @@ def create_review( session.refresh(db_review) return db_review -@router.get("/", response_model=List[ReviewRead]) +@router.get("/") def read_reviews( show_id: Optional[int] = None, venue_id: Optional[int] = None, @@ -70,4 +70,24 @@ def read_reviews( query = query.where(Review.year == year) reviews = session.exec(query.offset(offset).limit(limit)).all() - return reviews + + # Enrich with user profile data + result = [] + for review in reviews: + user = session.get(User, review.user_id) + profile = session.exec( + select(Profile).where(Profile.user_id == review.user_id) + ).first() + + result.append({ + **review.model_dump(), + "user": { + "id": user.id if user else review.user_id, + "username": profile.username if profile else f"User {review.user_id}", + "display_name": profile.display_name if profile else None, + "avatar_bg_color": user.avatar_bg_color if user else "#0F4C81", + "avatar_text": user.avatar_text if user else None, + } if user else None + }) + + return result diff --git a/backend/schemas.py b/backend/schemas.py index f13c669..92586ca 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -240,7 +240,7 @@ class CommentRead(CommentBase): # We might want to include the username here later class RatingBase(SQLModel): - score: float + score: Optional[float] = None # 1-5 stars, optional show_id: Optional[int] = None song_id: Optional[int] = None performance_id: Optional[int] = None @@ -256,9 +256,9 @@ class RatingRead(RatingBase): created_at: datetime class ReviewBase(SQLModel): - blurb: str - content: str - score: float + blurb: Optional[str] = None # Short tagline/summary + content: Optional[str] = None # Full review text + score: Optional[float] = None # Optional rating with review show_id: Optional[int] = None venue_id: Optional[int] = None song_id: Optional[int] = None diff --git a/frontend/components/reviews/review-card.tsx b/frontend/components/reviews/review-card.tsx index a972f30..6477117 100644 --- a/frontend/components/reviews/review-card.tsx +++ b/frontend/components/reviews/review-card.tsx @@ -1,14 +1,24 @@ +"use client" + import { Card, CardContent, CardHeader } from "@/components/ui/card" -import { StarRating } from "@/components/ui/star-rating" -import { formatDistanceToNow } from "date-fns" +import { UserAvatar } from "@/components/ui/user-avatar" + +interface ReviewUser { + id: number + username: string + display_name?: string | null + avatar_bg_color?: string + avatar_text?: string | null +} interface Review { id: number user_id: number - blurb: string - content: string - score: number + blurb?: string | null + content?: string | null + score?: number | null created_at: string + user?: ReviewUser | null } interface ReviewCardProps { @@ -16,24 +26,52 @@ interface ReviewCardProps { } export function ReviewCard({ review }: ReviewCardProps) { + // Format date in user's locale + const formattedDate = new Date(review.created_at).toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit' + }) + + const username = review.user?.display_name || review.user?.username || `User ${review.user_id}` + return ( -
-
-

"{review.blurb}"

-
- User #{review.user_id} - - {formatDistanceToNow(new Date(review.created_at), { addSuffix: true })} +
+
+ +
+ {review.blurb && ( +

"{review.blurb}"

+ )} +
+ {username} + + {formattedDate} +
- + {review.score !== null && review.score !== undefined && ( +
+ {review.score.toFixed(1)} + /10 +
+ )}
- -

{review.content}

-
+ {review.content && ( + +

{review.content}

+
+ )} ) }