Spaces:
Runtime error
Runtime error
Upload components/SpotifyDashboard.jsx with huggingface_hub
Browse files- components/SpotifyDashboard.jsx +101 -0
components/SpotifyDashboard.jsx
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Music, Clock, User as UserIcon, Disc } from 'lucide-react';
|
| 2 |
+
|
| 3 |
+
export default function SpotifyDashboard({ userData, topTracks, topArtists }) {
|
| 4 |
+
if (!userData) {
|
| 5 |
+
return (
|
| 6 |
+
<div className="h-full flex items-center justify-center text-gray-500">
|
| 7 |
+
<p>Connect to Spotify to view insights</p>
|
| 8 |
+
</div>
|
| 9 |
+
);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
return (
|
| 13 |
+
<div className="space-y-6 h-full overflow-y-auto pr-2">
|
| 14 |
+
{/* Profile Card */}
|
| 15 |
+
<div className="bg-gradient-to-br from-[#333] to-[#181818] rounded-xl p-6 border border-white/10 shadow-lg">
|
| 16 |
+
<div className="flex items-center gap-4">
|
| 17 |
+
{userData.images && userData.images.length > 0 ? (
|
| 18 |
+
<img
|
| 19 |
+
src={userData.images[0]?.url}
|
| 20 |
+
alt={userData.display_name}
|
| 21 |
+
className="w-20 h-20 rounded-full shadow-lg border-2 border-black/50"
|
| 22 |
+
/>
|
| 23 |
+
) : (
|
| 24 |
+
<div className="w-20 h-20 rounded-full bg-gray-700 flex items-center justify-center">
|
| 25 |
+
<UserIcon className="w-10 h-10 text-gray-400" />
|
| 26 |
+
</div>
|
| 27 |
+
)}
|
| 28 |
+
<div>
|
| 29 |
+
<p className="text-sm font-semibold text-gray-400">Profile</p>
|
| 30 |
+
<h2 className="text-2xl font-bold">{userData.display_name}</h2>
|
| 31 |
+
<div className="flex items-center gap-3 mt-2 text-sm text-gray-300">
|
| 32 |
+
<span className="bg-white/10 px-2 py-0.5 rounded text-xs">
|
| 33 |
+
{userData.product === 'premium' ? 'Premium' : 'Free'}
|
| 34 |
+
</span>
|
| 35 |
+
<span>{Math.floor(userData.followers?.total / 1000)}k Followers</span>
|
| 36 |
+
</div>
|
| 37 |
+
</div>
|
| 38 |
+
</div>
|
| 39 |
+
</div>
|
| 40 |
+
|
| 41 |
+
{/* Top Tracks */}
|
| 42 |
+
<div>
|
| 43 |
+
<h3 className="text-lg font-bold mb-4 flex items-center gap-2">
|
| 44 |
+
<Music className="w-5 h-5 text-spotify-green" />
|
| 45 |
+
Top Tracks
|
| 46 |
+
</h3>
|
| 47 |
+
<div className="space-y-2">
|
| 48 |
+
{topTracks?.slice(0, 5).map((track, index) => (
|
| 49 |
+
<div
|
| 50 |
+
key={track.id}
|
| 51 |
+
className="flex items-center gap-3 p-3 rounded-lg hover:bg-white/10 group transition-colors cursor-pointer"
|
| 52 |
+
>
|
| 53 |
+
<span className="text-gray-500 w-4 text-center font-mono text-sm">{index + 1}</span>
|
| 54 |
+
<img
|
| 55 |
+
src={track.album.images[0]?.url}
|
| 56 |
+
alt={track.name}
|
| 57 |
+
className="w-10 h-10 rounded shadow-md"
|
| 58 |
+
/>
|
| 59 |
+
<div className="flex-1 min-w-0">
|
| 60 |
+
<p className="font-medium text-sm truncate group-hover:text-spotify-green transition-colors">
|
| 61 |
+
{track.name}
|
| 62 |
+
</p>
|
| 63 |
+
<p className="text-xs text-gray-400 truncate">
|
| 64 |
+
{track.artists.map(a => a.name).join(', ')}
|
| 65 |
+
</p>
|
| 66 |
+
</div>
|
| 67 |
+
<div className="text-xs text-gray-500 flex items-center gap-1">
|
| 68 |
+
<Clock className="w-3 h-3" />
|
| 69 |
+
{Math.floor(track.duration_ms / 60000)}:{(track.duration_ms % 60000 / 1000).toFixed(0).padStart(2, '0')}
|
| 70 |
+
</div>
|
| 71 |
+
</div>
|
| 72 |
+
))}
|
| 73 |
+
</div>
|
| 74 |
+
</div>
|
| 75 |
+
|
| 76 |
+
{/* Top Artists */}
|
| 77 |
+
<div>
|
| 78 |
+
<h3 className="text-lg font-bold mb-4 flex items-center gap-2">
|
| 79 |
+
<Disc className="w-5 h-5 text-purple-400" />
|
| 80 |
+
Top Artists
|
| 81 |
+
</h3>
|
| 82 |
+
<div className="grid grid-cols-2 gap-3">
|
| 83 |
+
{topArtists?.slice(0, 4).map((artist) => (
|
| 84 |
+
<div
|
| 85 |
+
key={artist.id}
|
| 86 |
+
className="bg-white/5 rounded-lg p-3 hover:bg-white/10 transition-colors text-center"
|
| 87 |
+
>
|
| 88 |
+
<img
|
| 89 |
+
src={artist.images[0]?.url}
|
| 90 |
+
alt={artist.name}
|
| 91 |
+
className="w-full aspect-square object-cover rounded-md shadow-md mb-2"
|
| 92 |
+
/>
|
| 93 |
+
<p className="font-medium text-sm truncate">{artist.name}</p>
|
| 94 |
+
<p className="text-[10px] text-gray-400 capitalize">{artist.genres?.slice(0, 2).join(', ')}</p>
|
| 95 |
+
</div>
|
| 96 |
+
))}
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
);
|
| 101 |
+
}
|