From 5a8764df05ab907806b5e13b868919285a654669 Mon Sep 17 00:00:00 2001 From: fullsizemalt <106900403+fullsizemalt@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:06:37 -0800 Subject: [PATCH] feat: Add Heady Version Leaderboard to Song Page (Phase 4-5) - YouTube embed of #1 rated performance - Top 5 performances ranked with medal icons - Rating + rating count display - YouTube link icons for each performance - Gradient gold styling for heady section --- frontend/app/songs/[id]/page.tsx | 98 +++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/frontend/app/songs/[id]/page.tsx b/frontend/app/songs/[id]/page.tsx index a00c125..049f8b2 100644 --- a/frontend/app/songs/[id]/page.tsx +++ b/frontend/app/songs/[id]/page.tsx @@ -1,7 +1,7 @@ import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { ArrowLeft, PlayCircle, History, Calendar } from "lucide-react" +import { ArrowLeft, PlayCircle, History, Calendar, Trophy, Youtube, Star } from "lucide-react" import Link from "next/link" import { notFound } from "next/navigation" import { Badge } from "@/components/ui/badge" @@ -12,6 +12,7 @@ import { EntityReviews } from "@/components/reviews/entity-reviews" import { SocialWrapper } from "@/components/social/social-wrapper" import { PerformanceList } from "@/components/songs/performance-list" import { SongEvolutionChart } from "@/components/songs/song-evolution-chart" +import { YouTubeEmbed } from "@/components/ui/youtube-embed" async function getSong(id: string) { try { @@ -24,6 +25,15 @@ async function getSong(id: string) { } } +// Get top rated performances for "Heady Version" leaderboard +function getHeadyVersions(performances: any[]) { + if (!performances || performances.length === 0) return [] + return [...performances] + .filter(p => p.avg_rating && p.rating_count > 0) + .sort((a, b) => b.avg_rating - a.avg_rating) + .slice(0, 5) +} + export default async function SongDetailPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params const song = await getSong(id) @@ -32,6 +42,9 @@ export default async function SongDetailPage({ params }: { params: Promise<{ id: notFound() } + const headyVersions = getHeadyVersions(song.performances || []) + const topPerformance = headyVersions[0] + return (
No video available
++ {topPerformance.show?.date ? new Date(topPerformance.show.date).toLocaleDateString() : "Unknown Date"} +
++ {topPerformance.show?.venue?.name || "Unknown Venue"} +
++ {perf.show?.date ? new Date(perf.show.date).toLocaleDateString() : "Unknown"} +
++ {perf.show?.venue?.name || "Unknown Venue"} +
+