mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-06 13:11:25 +00:00
feat: mise en place de la messagerie [#17]
This commit is contained in:
196
Front-End/src/components/Chat/ConversationItem.js
Normal file
196
Front-End/src/components/Chat/ConversationItem.js
Normal file
@ -0,0 +1,196 @@
|
||||
import React from 'react';
|
||||
import { User, Trash2 } from 'lucide-react';
|
||||
import { getGravatarUrl } from '@/utils/gravatar';
|
||||
|
||||
const ConversationItem = ({
|
||||
conversation,
|
||||
isSelected,
|
||||
onClick,
|
||||
onDelete, // Nouvelle prop pour la suppression
|
||||
unreadCount = 0,
|
||||
lastMessage,
|
||||
isTyping = false,
|
||||
userPresences = {}, // Nouveau prop pour les statuts de présence
|
||||
}) => {
|
||||
const formatTime = (dateString) => {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInHours = (now - date) / (1000 * 60 * 60);
|
||||
|
||||
if (diffInHours < 24) {
|
||||
return date.toLocaleTimeString('fr-FR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
} else {
|
||||
return date.toLocaleDateString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getInterlocutorName = () => {
|
||||
if (conversation.interlocuteur) {
|
||||
// Si nous avons le nom et prénom, les utiliser
|
||||
if (
|
||||
conversation.interlocuteur.first_name &&
|
||||
conversation.interlocuteur.last_name
|
||||
) {
|
||||
return `${conversation.interlocuteur.first_name} ${conversation.interlocuteur.last_name}`;
|
||||
}
|
||||
// Sinon, utiliser l'email comme fallback
|
||||
if (conversation.interlocuteur.email) {
|
||||
return conversation.interlocuteur.email;
|
||||
}
|
||||
}
|
||||
return conversation.name || 'Utilisateur inconnu';
|
||||
};
|
||||
|
||||
const getLastMessageText = () => {
|
||||
if (isTyping) {
|
||||
return (
|
||||
<span className="text-emerald-500 italic">Tape un message...</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (lastMessage) {
|
||||
return lastMessage.content || lastMessage.corpus || 'Message...';
|
||||
}
|
||||
|
||||
if (conversation.last_message) {
|
||||
return (
|
||||
conversation.last_message.content ||
|
||||
conversation.last_message.corpus ||
|
||||
'Message...'
|
||||
);
|
||||
}
|
||||
|
||||
return 'Aucun message';
|
||||
};
|
||||
|
||||
const getLastMessageTime = () => {
|
||||
if (lastMessage) {
|
||||
return formatTime(lastMessage.created_at || lastMessage.date_envoi);
|
||||
}
|
||||
|
||||
if (conversation.last_message) {
|
||||
return formatTime(
|
||||
conversation.last_message.created_at ||
|
||||
conversation.last_message.date_envoi
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const getUserPresenceStatus = () => {
|
||||
if (conversation.interlocuteur?.id) {
|
||||
const presence = userPresences[conversation.interlocuteur.id];
|
||||
return presence?.status || 'offline';
|
||||
}
|
||||
return 'offline';
|
||||
};
|
||||
|
||||
const getPresenceColor = (status) => {
|
||||
switch (status) {
|
||||
case 'online':
|
||||
return 'bg-emerald-400';
|
||||
case 'away':
|
||||
return 'bg-yellow-400';
|
||||
case 'busy':
|
||||
return 'bg-red-400';
|
||||
case 'offline':
|
||||
default:
|
||||
return 'bg-gray-400';
|
||||
}
|
||||
};
|
||||
|
||||
const getPresenceLabel = (status) => {
|
||||
switch (status) {
|
||||
case 'online':
|
||||
return 'En ligne';
|
||||
case 'away':
|
||||
return 'Absent';
|
||||
case 'busy':
|
||||
return 'Occupé';
|
||||
case 'offline':
|
||||
default:
|
||||
return 'Hors ligne';
|
||||
}
|
||||
};
|
||||
|
||||
const presenceStatus = getUserPresenceStatus();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`group flex items-center p-3 cursor-pointer rounded-lg transition-all duration-200 hover:bg-gray-50 ${
|
||||
isSelected
|
||||
? 'bg-emerald-50 border-l-4 border-emerald-500'
|
||||
: 'hover:bg-gray-50'
|
||||
}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{/* Avatar */}
|
||||
<div className="relative">
|
||||
<img
|
||||
src={getGravatarUrl(
|
||||
conversation.interlocuteur?.email || 'default',
|
||||
48
|
||||
)}
|
||||
alt={`Avatar de ${getInterlocutorName()}`}
|
||||
className="w-12 h-12 rounded-full object-cover shadow-md"
|
||||
/>
|
||||
|
||||
{/* Indicateur de statut en ligne */}
|
||||
<div
|
||||
className={`absolute -bottom-0.5 -right-0.5 w-4 h-4 ${getPresenceColor(presenceStatus)} border-2 border-white rounded-full`}
|
||||
title={getPresenceLabel(presenceStatus)}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
{/* Contenu de la conversation */}
|
||||
<div className="flex-1 ml-3 overflow-hidden">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3
|
||||
className={`font-semibold truncate ${
|
||||
isSelected ? 'text-emerald-700' : 'text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{getInterlocutorName()}
|
||||
</h3>
|
||||
<div className="flex items-center space-x-2">
|
||||
{unreadCount > 0 && (
|
||||
<span className="bg-red-500 text-white text-xs rounded-full w-4 h-4 text-center"></span>
|
||||
)}
|
||||
<span className="text-xs text-gray-500">
|
||||
{getLastMessageTime()}
|
||||
</span>
|
||||
{/* Bouton de suppression */}
|
||||
{onDelete && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // Empêcher la sélection de la conversation
|
||||
onDelete();
|
||||
}}
|
||||
className="opacity-0 group-hover:opacity-100 hover:bg-red-100 p-1 rounded transition-all duration-200"
|
||||
title="Supprimer la conversation"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 text-red-500 hover:text-red-700" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
className={`text-sm truncate mt-1 ${isTyping ? '' : 'text-gray-600'}`}
|
||||
>
|
||||
{getLastMessageText()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConversationItem;
|
||||
Reference in New Issue
Block a user