Teal.fm frontend powered by slices.network tealfm-slices.wisp.place
tealfm slices
1import { useAlbumArt } from "./useAlbumArt"; 2 3interface Artist { 4 artistName: string; 5} 6 7interface TopTrackItemProps { 8 trackName: string; 9 releaseMbId: string | null | undefined; 10 artists: string | null | undefined; 11 count: number; 12 rank: number; 13 maxCount: number; 14} 15 16export default function TopTrackItem({ 17 trackName, 18 releaseMbId, 19 artists, 20 count, 21 rank, 22 maxCount, 23}: TopTrackItemProps) { 24 const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId); 25 const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0; 26 27 // Parse artists JSON 28 let artistNames = "Unknown Artist"; 29 if (artists) { 30 try { 31 const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists; 32 if (Array.isArray(parsed)) { 33 artistNames = parsed.map((a: Artist) => a.artistName).join(", "); 34 } else if (typeof parsed === 'string') { 35 artistNames = parsed; 36 } 37 } catch { 38 artistNames = String(artists); 39 } 40 } 41 42 return ( 43 <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden"> 44 <div 45 className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all" 46 style={{ width: `${barWidth}%` }} 47 /> 48 <div className="flex items-center gap-4 relative"> 49 <div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium"> 50 {rank} 51 </div> 52 53 <div className="flex-shrink-0"> 54 {isLoading ? ( 55 <div className="w-10 h-10 bg-zinc-800 animate-pulse" /> 56 ) : albumArtUrl ? ( 57 <img 58 src={albumArtUrl} 59 alt={`${trackName} album art`} 60 className="w-10 h-10 object-cover" 61 loading="lazy" 62 /> 63 ) : ( 64 <div className="w-10 h-10 bg-zinc-800 flex items-center justify-center"> 65 <svg 66 className="w-5 h-5 text-zinc-600" 67 fill="currentColor" 68 viewBox="0 0 20 20" 69 > 70 <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" /> 71 </svg> 72 </div> 73 )} 74 </div> 75 76 <div className="flex-1 min-w-0"> 77 <h3 className="text-sm font-medium text-zinc-100 truncate"> 78 {trackName} 79 </h3> 80 <p className="text-xs text-zinc-500 truncate">{artistNames}</p> 81 </div> 82 83 <div className="text-right flex-shrink-0"> 84 <p className="text-xs text-zinc-400 font-medium"> 85 {count.toLocaleString()} 86 </p> 87 </div> 88 </div> 89 </div> 90 ); 91}