188 lines
6.0 KiB
TypeScript
188 lines
6.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { InvitationCard } from '@/components/InvitationCard'
|
|
import { InviteArtistModal } from '@/components/InviteArtistModal'
|
|
|
|
export default function LabelInvitationsPage() {
|
|
const [label, setLabel] = useState<any>(null)
|
|
const [invitations, setInvitations] = useState<any[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [isInviteModalOpen, setIsInviteModalOpen] = useState(false)
|
|
const [filter, setFilter] = useState<'all' | 'pending' | 'accepted' | 'declined'>('all')
|
|
|
|
useEffect(() => {
|
|
fetchInvitations()
|
|
}, [])
|
|
|
|
const fetchInvitations = 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 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 invitations:', error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleCancelInvitation = async (invitationId: string) => {
|
|
if (!confirm('Are you sure you want to cancel this invitation?')) {
|
|
return
|
|
}
|
|
|
|
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')
|
|
}
|
|
}
|
|
|
|
const filteredInvitations = invitations.filter(inv => {
|
|
if (filter === 'all') return true
|
|
return inv.status === filter.toUpperCase()
|
|
})
|
|
|
|
const counts = {
|
|
all: invitations.length,
|
|
pending: invitations.filter(inv => inv.status === 'PENDING').length,
|
|
accepted: invitations.filter(inv => inv.status === 'ACCEPTED').length,
|
|
declined: invitations.filter(inv => inv.status === 'DECLINED').length
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<main className="min-h-screen bg-zinc-950 flex items-center justify-center">
|
|
<p className="text-zinc-400">Loading invitations...</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">Label not found</p>
|
|
<a
|
|
href="/label/dashboard"
|
|
className="px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white font-semibold rounded-lg inline-block"
|
|
>
|
|
Go to Dashboard
|
|
</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">Invitations</h1>
|
|
<p className="text-xl text-zinc-400">Manage your artist invitations</p>
|
|
</div>
|
|
<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>
|
|
|
|
{/* Filter Tabs */}
|
|
<div className="mb-8 flex gap-2 border-b border-zinc-800">
|
|
{(['all', 'pending', 'accepted', 'declined'] as const).map((status) => (
|
|
<button
|
|
key={status}
|
|
onClick={() => setFilter(status)}
|
|
className={`px-6 py-3 font-medium transition-colors ${
|
|
filter === status
|
|
? 'text-purple-400 border-b-2 border-purple-400'
|
|
: 'text-zinc-400 hover:text-white'
|
|
}`}
|
|
>
|
|
{status.charAt(0).toUpperCase() + status.slice(1)}
|
|
<span className="ml-2 text-sm">({counts[status]})</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Invitations List */}
|
|
{filteredInvitations.length > 0 ? (
|
|
<div className="space-y-4">
|
|
{filteredInvitations.map((invitation) => (
|
|
<InvitationCard
|
|
key={invitation.id}
|
|
invitation={invitation}
|
|
viewType="label"
|
|
onCancel={handleCancelInvitation}
|
|
/>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-12">
|
|
<svg className="w-16 h-16 text-zinc-600 mx-auto mb-4" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
|
|
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
|
|
</svg>
|
|
<p className="text-zinc-400 text-lg">
|
|
{filter === 'all'
|
|
? 'No invitations yet'
|
|
: `No ${filter} invitations`}
|
|
</p>
|
|
<button
|
|
onClick={() => setIsInviteModalOpen(true)}
|
|
className="mt-4 px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-lg transition"
|
|
>
|
|
Send Your First Invitation
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Invite Modal */}
|
|
<InviteArtistModal
|
|
isOpen={isInviteModalOpen}
|
|
labelId={label.id}
|
|
onClose={() => setIsInviteModalOpen(false)}
|
|
onInviteSent={() => {
|
|
setIsInviteModalOpen(false)
|
|
fetchInvitations()
|
|
}}
|
|
/>
|
|
</main>
|
|
)
|
|
}
|