Files
spotlightcam/frontend/src/components/matches/MatchCard.jsx

120 lines
4.6 KiB
React
Raw Normal View History

import { Link } from 'react-router-dom';
import { MessageCircle, Check, X, Loader2, Calendar, MapPin } from 'lucide-react';
import { MATCH_STATUS } from '../../constants';
/**
* 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 === MATCH_STATUS.PENDING;
const isOutgoing = match.isInitiator && match.status === MATCH_STATUS.PENDING;
const isAccepted = match.status === MATCH_STATUS.ACCEPTED;
return (
<div className="w-full 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;