project-standalo-sonic-cloud/app/label/invitations/page.tsx

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>
)
}