148 lines
4.6 KiB
TypeScript
148 lines
4.6 KiB
TypeScript
'use client'
|
|
|
|
export interface InvitationCardProps {
|
|
invitation: {
|
|
id: string
|
|
status: 'PENDING' | 'ACCEPTED' | 'DECLINED'
|
|
message?: string
|
|
createdAt: string
|
|
label?: {
|
|
id: string
|
|
name: string
|
|
logoUrl?: string
|
|
}
|
|
artist?: {
|
|
id: string
|
|
name: string
|
|
user?: {
|
|
avatarUrl?: string
|
|
}
|
|
}
|
|
}
|
|
viewType: 'label' | 'artist'
|
|
onAccept?: (invitationId: string) => void
|
|
onDecline?: (invitationId: string) => void
|
|
onCancel?: (invitationId: string) => void
|
|
}
|
|
|
|
export function InvitationCard({
|
|
invitation,
|
|
viewType,
|
|
onAccept,
|
|
onDecline,
|
|
onCancel
|
|
}: InvitationCardProps) {
|
|
const formatDate = (dateString: string) => {
|
|
const date = new Date(dateString)
|
|
return date.toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric'
|
|
})
|
|
}
|
|
|
|
const statusColors = {
|
|
PENDING: 'bg-yellow-900/20 text-yellow-400 border-yellow-800',
|
|
ACCEPTED: 'bg-green-900/20 text-green-400 border-green-800',
|
|
DECLINED: 'bg-red-900/20 text-red-400 border-red-800'
|
|
}
|
|
|
|
const isPending = invitation.status === 'PENDING'
|
|
|
|
return (
|
|
<div className="bg-zinc-900 rounded-lg p-6 border border-zinc-800">
|
|
<div className="flex items-start gap-4">
|
|
{/* Avatar/Logo */}
|
|
<div className="w-16 h-16 rounded-lg overflow-hidden bg-zinc-800 flex-shrink-0">
|
|
{viewType === 'artist' && invitation.label?.logoUrl ? (
|
|
<img
|
|
src={invitation.label.logoUrl}
|
|
alt={invitation.label.name}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : viewType === 'label' && invitation.artist?.user?.avatarUrl ? (
|
|
<img
|
|
src={invitation.artist.user.avatarUrl}
|
|
alt={invitation.artist.name}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center">
|
|
<svg className="w-8 h-8 text-zinc-600" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M10 2a8 8 0 100 16 8 8 0 000-16zM8 9a1 1 0 000 2h4a1 1 0 100-2H8z" />
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-start justify-between gap-4 mb-2">
|
|
<div>
|
|
<h3 className="font-semibold text-white truncate">
|
|
{viewType === 'artist' && invitation.label
|
|
? invitation.label.name
|
|
: viewType === 'label' && invitation.artist
|
|
? invitation.artist.name
|
|
: 'Unknown'}
|
|
</h3>
|
|
<p className="text-sm text-zinc-400">
|
|
{viewType === 'artist'
|
|
? 'Invited you to join their label'
|
|
: 'Invitation sent'}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Status Badge */}
|
|
<span className={`px-3 py-1 text-xs font-medium rounded-full border ${statusColors[invitation.status]}`}>
|
|
{invitation.status}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Message */}
|
|
{invitation.message && (
|
|
<p className="text-sm text-zinc-400 mb-3 line-clamp-2">
|
|
{invitation.message}
|
|
</p>
|
|
)}
|
|
|
|
{/* Date */}
|
|
<p className="text-xs text-zinc-500 mb-4">
|
|
{formatDate(invitation.createdAt)}
|
|
</p>
|
|
|
|
{/* Actions */}
|
|
{isPending && (
|
|
<div className="flex gap-2">
|
|
{viewType === 'artist' && onAccept && onDecline && (
|
|
<>
|
|
<button
|
|
onClick={() => onAccept(invitation.id)}
|
|
className="flex-1 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium rounded-lg transition"
|
|
>
|
|
Accept
|
|
</button>
|
|
<button
|
|
onClick={() => onDecline(invitation.id)}
|
|
className="flex-1 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-white text-sm font-medium rounded-lg transition"
|
|
>
|
|
Decline
|
|
</button>
|
|
</>
|
|
)}
|
|
{viewType === 'label' && onCancel && (
|
|
<button
|
|
onClick={() => onCancel(invitation.id)}
|
|
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm font-medium rounded-lg transition"
|
|
>
|
|
Cancel Invitation
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|