122 lines
3.9 KiB
TypeScript
122 lines
3.9 KiB
TypeScript
import { ArtistHeader } from '@/components/ArtistHeader'
|
|
import { AlbumCard } from '@/components/AlbumCard'
|
|
import { SongCard } from '@/components/SongCard'
|
|
import { SocialLinks } from '@/components/SocialLinks'
|
|
import { SectionHeader } from '@/components/SectionHeader'
|
|
|
|
interface PageProps {
|
|
params: Promise<{ id: string }>
|
|
}
|
|
|
|
async function getArtist(id: string) {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/artists/${id}`, {
|
|
cache: 'no-store',
|
|
})
|
|
if (!res.ok) throw new Error('Artist not found')
|
|
const data = await res.json()
|
|
return data.artist
|
|
}
|
|
|
|
async function getArtistSongs(id: string) {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/artists/${id}/songs`, {
|
|
cache: 'no-store',
|
|
})
|
|
if (!res.ok) return []
|
|
const data = await res.json()
|
|
return data.songs || []
|
|
}
|
|
|
|
async function getArtistAlbums(id: string) {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/artists/${id}/albums`, {
|
|
cache: 'no-store',
|
|
})
|
|
if (!res.ok) return []
|
|
const data = await res.json()
|
|
return data.albums || []
|
|
}
|
|
|
|
export default async function ArtistPage({ params }: PageProps) {
|
|
const { id } = await params
|
|
|
|
const [artist, songs, albums] = await Promise.all([
|
|
getArtist(id),
|
|
getArtistSongs(id),
|
|
getArtistAlbums(id),
|
|
])
|
|
|
|
return (
|
|
<main className="min-h-screen bg-zinc-950">
|
|
<ArtistHeader
|
|
name={artist.name}
|
|
avatarUrl={artist.user?.avatarUrl}
|
|
bio={artist.bio}
|
|
verified={artist.verified}
|
|
/>
|
|
|
|
<div className="max-w-7xl mx-auto px-4 py-8">
|
|
{/* Social Links */}
|
|
{(artist.website || artist.twitter || artist.instagram || artist.spotify) && (
|
|
<div className="mb-8">
|
|
<SocialLinks
|
|
links={[
|
|
...(artist.website ? [{ platform: 'website' as const, url: artist.website }] : []),
|
|
...(artist.twitter ? [{ platform: 'twitter' as const, url: artist.twitter }] : []),
|
|
...(artist.instagram ? [{ platform: 'instagram' as const, url: artist.instagram }] : []),
|
|
...(artist.spotify ? [{ platform: 'spotify' as const, url: artist.spotify }] : []),
|
|
]}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Popular Tracks */}
|
|
{songs.length > 0 && (
|
|
<section className="mb-12">
|
|
<SectionHeader title="Popular Tracks" />
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mt-6">
|
|
{songs.map((song: any) => (
|
|
<SongCard
|
|
key={song.id}
|
|
id={song.id}
|
|
title={song.title}
|
|
artistName={artist.name}
|
|
coverUrl={song.coverUrl || song.album?.coverUrl}
|
|
duration={song.duration || 0}
|
|
plays={song.plays}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Albums */}
|
|
{albums.length > 0 && (
|
|
<section>
|
|
<SectionHeader title="Albums" />
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mt-6">
|
|
{albums.map((album: any) => (
|
|
<AlbumCard
|
|
key={album.id}
|
|
id={album.id}
|
|
title={album.title}
|
|
artistName={artist.name}
|
|
coverUrl={album.coverUrl}
|
|
releaseYear={album.releaseDate ? new Date(album.releaseDate).getFullYear() : undefined}
|
|
trackCount={album._count?.songs || album.songs?.length}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Bio */}
|
|
{artist.bio && (
|
|
<section className="mt-12">
|
|
<h2 className="text-2xl font-bold text-white mb-4">About</h2>
|
|
<p className="text-zinc-300 leading-relaxed max-w-3xl">{artist.bio}</p>
|
|
</section>
|
|
)}
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|