210 lines
6.3 KiB
TypeScript
210 lines
6.3 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { LabelStats } from '@/components/LabelStats'
|
|
import { ArtistRoster } from '@/components/ArtistRoster'
|
|
import { InvitationCard } from '@/components/InvitationCard'
|
|
import { InviteArtistModal } from '@/components/InviteArtistModal'
|
|
|
|
export default function LabelDashboardPage() {
|
|
const [label, setLabel] = useState<any>(null)
|
|
const [stats, setStats] = useState<any>(null)
|
|
const [invitations, setInvitations] = useState<any[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [isInviteModalOpen, setIsInviteModalOpen] = useState(false)
|
|
|
|
useEffect(() => {
|
|
fetchDashboardData()
|
|
}, [])
|
|
|
|
const fetchDashboardData = async () => {
|
|
try {
|
|
// Check authentication
|
|
const userRes = await fetch('/api/users/me')
|
|
if (!userRes.ok) {
|
|
window.location.href = '/login'
|
|
return
|
|
}
|
|
const userData = await userRes.json()
|
|
|
|
// Fetch user's label
|
|
const labelRes = await fetch(`/api/users/${userData.user.id}/label`)
|
|
if (!labelRes.ok) {
|
|
window.location.href = '/label/create'
|
|
return
|
|
}
|
|
const labelData = await labelRes.json()
|
|
setLabel(labelData.label)
|
|
|
|
// Fetch stats
|
|
const statsRes = await fetch(`/api/labels/${labelData.label.id}/stats`)
|
|
if (statsRes.ok) {
|
|
const statsData = await statsRes.json()
|
|
setStats(statsData.stats)
|
|
}
|
|
|
|
// Fetch invitations
|
|
const invitationsRes = await fetch(`/api/labels/${labelData.label.id}/invitations`)
|
|
if (invitationsRes.ok) {
|
|
const invitationsData = await invitationsRes.json()
|
|
setInvitations(invitationsData.invitations || [])
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch dashboard data:', error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleRemoveArtist = async (artistId: string) => {
|
|
if (!confirm('Are you sure you want to remove this artist from your label?')) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`/api/labels/${label.id}/artists/${artistId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
|
|
if (res.ok) {
|
|
fetchDashboardData()
|
|
} else {
|
|
alert('Failed to remove artist')
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to remove artist:', error)
|
|
alert('Network error')
|
|
}
|
|
}
|
|
|
|
const handleCancelInvitation = async (invitationId: string) => {
|
|
try {
|
|
const res = await fetch(`/api/invitations/${invitationId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
|
|
if (res.ok) {
|
|
setInvitations(invitations.filter(inv => inv.id !== invitationId))
|
|
} else {
|
|
alert('Failed to cancel invitation')
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to cancel invitation:', error)
|
|
alert('Network error')
|
|
}
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<main className="min-h-screen bg-zinc-950 flex items-center justify-center">
|
|
<p className="text-zinc-400">Loading dashboard...</p>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
if (!label) {
|
|
return (
|
|
<main className="min-h-screen bg-zinc-950 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-xl text-zinc-400 mb-4">You don't have a label yet</p>
|
|
<a
|
|
href="/label/create"
|
|
className="px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white font-semibold rounded-lg inline-block"
|
|
>
|
|
Create Label
|
|
</a>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<main className="min-h-screen bg-zinc-950">
|
|
<div className="max-w-7xl mx-auto px-4 py-8">
|
|
{/* Header */}
|
|
<div className="mb-8 flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-4xl font-bold text-white mb-2">{label.name}</h1>
|
|
<p className="text-xl text-zinc-400">Label Dashboard</p>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
<a
|
|
href="/label/settings"
|
|
className="px-6 py-3 bg-zinc-800 hover:bg-zinc-700 text-white font-medium rounded-lg transition"
|
|
>
|
|
Settings
|
|
</a>
|
|
<button
|
|
onClick={() => setIsInviteModalOpen(true)}
|
|
className="px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-lg transition"
|
|
>
|
|
Invite Artist
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats */}
|
|
{stats && (
|
|
<section className="mb-12">
|
|
<LabelStats stats={stats} />
|
|
</section>
|
|
)}
|
|
|
|
{/* Artist Roster */}
|
|
<section className="mb-12">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h2 className="text-2xl font-bold text-white">Your Artists</h2>
|
|
<button
|
|
onClick={() => setIsInviteModalOpen(true)}
|
|
className="text-purple-400 hover:text-purple-300 font-medium"
|
|
>
|
|
+ Invite Artist
|
|
</button>
|
|
</div>
|
|
<ArtistRoster
|
|
artists={label.artists || []}
|
|
isOwner={true}
|
|
emptyMessage="No artists signed yet. Start inviting artists to join your label!"
|
|
onRemoveArtist={handleRemoveArtist}
|
|
onArtistClick={(artistId) => {
|
|
window.location.href = `/artist/${artistId}`
|
|
}}
|
|
/>
|
|
</section>
|
|
|
|
{/* Pending Invitations */}
|
|
{invitations.length > 0 && (
|
|
<section>
|
|
<h2 className="text-2xl font-bold text-white mb-6">
|
|
Pending Invitations ({invitations.filter(inv => inv.status === 'PENDING').length})
|
|
</h2>
|
|
<div className="space-y-4">
|
|
{invitations
|
|
.filter(inv => inv.status === 'PENDING')
|
|
.map((invitation) => (
|
|
<InvitationCard
|
|
key={invitation.id}
|
|
invitation={invitation}
|
|
viewType="label"
|
|
onCancel={handleCancelInvitation}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
</div>
|
|
|
|
{/* Invite Modal */}
|
|
<InviteArtistModal
|
|
isOpen={isInviteModalOpen}
|
|
labelId={label.id}
|
|
onClose={() => setIsInviteModalOpen(false)}
|
|
onInviteSent={() => {
|
|
setIsInviteModalOpen(false)
|
|
fetchDashboardData()
|
|
}}
|
|
/>
|
|
</main>
|
|
)
|
|
}
|