fediversion/frontend/components/reviews/entity-reviews.tsx
fullsizemalt b4cddf41ea feat: Initialize Fediversion multi-band platform
- Fork elmeg-demo codebase for multi-band support
- Add data importer infrastructure with base class
- Create band-specific importers:
  - phish.py: Phish.net API v5
  - grateful_dead.py: Grateful Stats API
  - setlistfm.py: Dead & Company, Billy Strings (Setlist.fm)
- Add spec-kit configuration for Gemini
- Update README with supported bands and architecture
2025-12-28 12:39:28 -08:00

135 lines
3.8 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
import { ReviewCard } from "@/components/reviews/review-card"
import { ReviewForm } from "@/components/reviews/review-form"
import { getApiUrl } from "@/lib/api-config"
interface Review {
id: number
user_id: number
blurb?: string | null
content?: string | null
score?: number | null
created_at: string
user?: {
id: number
username: string
display_name?: string | null
avatar_bg_color?: string
avatar_text?: string | null
} | null
}
export type EntityType = "show" | "venue" | "song" | "performance" | "tour" | "year"
interface EntityReviewsProps {
entityType: EntityType
entityId: number
entityName?: string // e.g., "Arcadia"
entityContext?: string // e.g., "Sat, Mar 14, 2015"
initialReviews?: Review[]
}
export function EntityReviews({
entityType,
entityId,
entityName,
entityContext,
initialReviews = []
}: EntityReviewsProps) {
const [reviews, setReviews] = useState<Review[]>(initialReviews)
// Fetch reviews on mount if not provided (or to refresh)
useEffect(() => {
if (initialReviews.length === 0) {
fetchReviews()
}
}, [entityType, entityId])
const fetchReviews = async () => {
try {
const queryParam = entityType === 'year' ? 'year' : `${entityType}_id`
const res = await fetch(`${getApiUrl()}/reviews/?${queryParam}=${entityId}`)
if (res.ok) {
const data = await res.json()
setReviews(data)
}
} catch (err) {
console.error("Failed to fetch reviews", err)
}
}
const handleSubmit = async (data: { blurb: string; content: string; score: number }) => {
const token = localStorage.getItem("token")
if (!token) {
alert("Please log in to leave a review.")
return
}
try {
const body: any = { ...data }
if (entityType === 'year') {
body.year = entityId
} else {
body[`${entityType}_id`] = entityId
}
const res = await fetch(`${getApiUrl()}/reviews/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify(body)
})
if (!res.ok) throw new Error("Failed to post review")
const newReview = await res.json()
setReviews(prev => [newReview, ...prev])
alert("Review posted!")
} catch (err) {
console.error(err)
alert("Error posting review")
}
}
// Build title with context
const getTitle = () => {
if (entityName && entityContext) {
return `Write a Review for ${entityName}`
}
if (entityName) {
return `Write a Review for ${entityName}`
}
return "Write a Review"
}
return (
<div className="space-y-6 pt-6 border-t">
<h2 className="text-2xl font-bold">Reviews</h2>
<ReviewForm
onSubmit={handleSubmit}
title={getTitle()}
subtitle={entityContext}
/>
<div className="space-y-4">
{reviews.length === 0 ? (
<p className="text-muted-foreground">No reviews yet. Be the first!</p>
) : (
reviews.map(review => (
<ReviewCard
key={review.id}
review={review}
/>
))
)}
</div>
</div>
)
}