94 lines
2.7 KiB
React
94 lines
2.7 KiB
React
|
|
import { Link } from 'react-router-dom';
|
||
|
|
import Avatar from '../common/Avatar';
|
||
|
|
import HeatBadges from '../heats/HeatBadges';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reusable User List Item component
|
||
|
|
* Displays user avatar, username, and optional heats with action button
|
||
|
|
*
|
||
|
|
* @param {object} user - User object with { userId, username, avatar, isOnline, firstName, lastName }
|
||
|
|
* @param {Array} heats - Array of heat objects for this user
|
||
|
|
* @param {boolean} showHeats - Whether to display heat badges (default: true)
|
||
|
|
* @param {React.ReactNode} actionButton - Optional action button/element to display on right
|
||
|
|
* @param {function} onClick - Optional click handler for the entire item
|
||
|
|
* @param {string} className - Additional CSS classes
|
||
|
|
* @param {boolean} linkToProfile - Whether username should link to profile (default: false)
|
||
|
|
*
|
||
|
|
* @example
|
||
|
|
* <UserListItem
|
||
|
|
* user={displayUser}
|
||
|
|
* heats={userHeats.get(userId)}
|
||
|
|
* actionButton={
|
||
|
|
* <button onClick={() => handleMatch(userId)}>
|
||
|
|
* <UserPlus />
|
||
|
|
* </button>
|
||
|
|
* }
|
||
|
|
* />
|
||
|
|
*/
|
||
|
|
const UserListItem = ({
|
||
|
|
user,
|
||
|
|
heats = [],
|
||
|
|
showHeats = true,
|
||
|
|
actionButton,
|
||
|
|
onClick,
|
||
|
|
className = '',
|
||
|
|
linkToProfile = false
|
||
|
|
}) => {
|
||
|
|
const hasHeats = heats && heats.length > 0;
|
||
|
|
const isOnline = user?.isOnline ?? false;
|
||
|
|
|
||
|
|
const usernameClasses = `text-sm font-medium truncate ${
|
||
|
|
isOnline ? 'text-gray-900' : 'text-gray-500'
|
||
|
|
}`;
|
||
|
|
|
||
|
|
const usernameContent = linkToProfile ? (
|
||
|
|
<Link
|
||
|
|
to={`/profile/${user.username}`}
|
||
|
|
className={`${usernameClasses} hover:text-primary-600`}
|
||
|
|
>
|
||
|
|
{user.username}
|
||
|
|
</Link>
|
||
|
|
) : (
|
||
|
|
<p className={usernameClasses}>{user.username}</p>
|
||
|
|
);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className={`flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg ${className}`}
|
||
|
|
onClick={onClick}
|
||
|
|
>
|
||
|
|
<div className="flex items-center space-x-2 flex-1 min-w-0">
|
||
|
|
<Avatar
|
||
|
|
src={user.avatar}
|
||
|
|
username={user.username}
|
||
|
|
size={32}
|
||
|
|
status={isOnline ? 'online' : 'offline'}
|
||
|
|
title={user.username}
|
||
|
|
/>
|
||
|
|
<div className="flex-1 min-w-0">
|
||
|
|
{usernameContent}
|
||
|
|
{/* Full name (optional) */}
|
||
|
|
{(user.firstName || user.lastName) && (
|
||
|
|
<p className="text-xs text-gray-500 truncate">
|
||
|
|
{user.firstName} {user.lastName}
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
{/* Heat Badges */}
|
||
|
|
{showHeats && hasHeats && (
|
||
|
|
<div className="mt-1">
|
||
|
|
<HeatBadges heats={heats} maxVisible={3} />
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{actionButton && (
|
||
|
|
<div className="flex-shrink-0 ml-2">
|
||
|
|
{actionButton}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default UserListItem;
|