119 lines
4.5 KiB
React
119 lines
4.5 KiB
React
|
|
import { Link } from 'react-router-dom';
|
||
|
|
import { MessageCircle, Check, X, Loader2, Calendar, MapPin } from 'lucide-react';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Match card for matches list page
|
||
|
|
* Shows match details with accept/reject/chat actions
|
||
|
|
*/
|
||
|
|
const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
||
|
|
const isIncoming = !match.isInitiator && match.status === 'pending';
|
||
|
|
const isOutgoing = match.isInitiator && match.status === 'pending';
|
||
|
|
const isAccepted = match.status === 'accepted';
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 hover:shadow-md transition-shadow">
|
||
|
|
<div className="flex items-start justify-between">
|
||
|
|
<div className="flex-1">
|
||
|
|
<div className="flex items-center gap-3 mb-2">
|
||
|
|
<Link to={`/${match.partner.username}`} className="flex-shrink-0">
|
||
|
|
<img
|
||
|
|
src={match.partner.avatar || `https://api.dicebear.com/7.x/avataaars/svg?seed=${match.partner.username}`}
|
||
|
|
alt={match.partner.username}
|
||
|
|
className="w-12 h-12 rounded-full hover:ring-2 hover:ring-primary-500 transition-all"
|
||
|
|
/>
|
||
|
|
</Link>
|
||
|
|
<div>
|
||
|
|
<Link to={`/${match.partner.username}`}>
|
||
|
|
<h3 className="font-semibold text-gray-900 hover:text-primary-600 transition-colors">
|
||
|
|
{match.partner.firstName && match.partner.lastName
|
||
|
|
? `${match.partner.firstName} ${match.partner.lastName}`
|
||
|
|
: match.partner.username}
|
||
|
|
</h3>
|
||
|
|
</Link>
|
||
|
|
<p className="text-sm text-gray-600">@{match.partner.username}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-4 text-sm text-gray-600 mb-2">
|
||
|
|
<div className="flex items-center gap-1">
|
||
|
|
<Calendar className="w-4 h-4" />
|
||
|
|
<span>{match.event.name}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-1">
|
||
|
|
<MapPin className="w-4 h-4" />
|
||
|
|
<span>{match.event.location}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
{isIncoming && (
|
||
|
|
<span className="text-xs px-2 py-1 bg-amber-100 text-amber-700 rounded-full font-medium">
|
||
|
|
Incoming Request
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
{isOutgoing && (
|
||
|
|
<span className="text-xs px-2 py-1 bg-blue-100 text-blue-700 rounded-full font-medium">
|
||
|
|
Sent Request
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
{isAccepted && (
|
||
|
|
<span className="text-xs px-2 py-1 bg-green-100 text-green-700 rounded-full font-medium">
|
||
|
|
Active Match
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-2 ml-4">
|
||
|
|
{isIncoming && (
|
||
|
|
<>
|
||
|
|
<button
|
||
|
|
onClick={() => onAccept(match.slug)}
|
||
|
|
disabled={processing}
|
||
|
|
className="p-2 bg-green-600 text-white rounded-full hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
|
|
title="Accept"
|
||
|
|
>
|
||
|
|
{processing ? (
|
||
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
||
|
|
) : (
|
||
|
|
<Check className="w-5 h-5" />
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={() => onReject(match.slug)}
|
||
|
|
disabled={processing}
|
||
|
|
className="p-2 bg-red-600 text-white rounded-full hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
|
|
title="Reject"
|
||
|
|
>
|
||
|
|
<X className="w-5 h-5" />
|
||
|
|
</button>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{isOutgoing && (
|
||
|
|
<button
|
||
|
|
onClick={() => onReject(match.slug)}
|
||
|
|
disabled={processing}
|
||
|
|
className="px-3 py-1.5 text-sm border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
|
|
>
|
||
|
|
Cancel Request
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{isAccepted && (
|
||
|
|
<button
|
||
|
|
onClick={() => onOpenChat(match)}
|
||
|
|
className="flex items-center gap-2 px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 transition-colors"
|
||
|
|
>
|
||
|
|
<MessageCircle className="w-4 h-4" />
|
||
|
|
Open Chat
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default MatchCard;
|