179 lines
No EOL
5.9 KiB
TypeScript
179 lines
No EOL
5.9 KiB
TypeScript
import React, { useState } from "react";
|
|
import { ChevronDown, ChevronRight, User, Settings, Paperclip } from "lucide-react";
|
|
|
|
type Message = {
|
|
id: string;
|
|
content: string;
|
|
author_type: "client" | "staff";
|
|
author_name?: string;
|
|
created_at: string;
|
|
attachments?: Array<{
|
|
filename: string;
|
|
url: string;
|
|
size?: number;
|
|
}>;
|
|
};
|
|
|
|
interface MessageCardProps {
|
|
message: Message;
|
|
isExpanded: boolean;
|
|
onToggle: () => void;
|
|
}
|
|
|
|
function formatTimeAgo(dateString: string): string {
|
|
const date = new Date(dateString);
|
|
const now = new Date();
|
|
const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1000 * 60));
|
|
|
|
if (diffInMinutes < 1) return "À l'instant";
|
|
if (diffInMinutes < 60) return `Il y a ${diffInMinutes} min`;
|
|
|
|
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
if (diffInHours < 24) return `Il y a ${diffInHours}h`;
|
|
|
|
const diffInDays = Math.floor(diffInHours / 24);
|
|
if (diffInDays < 7) return `Il y a ${diffInDays}j`;
|
|
|
|
return date.toLocaleDateString('fr-FR');
|
|
}
|
|
|
|
function formatFileSize(bytes?: number): string {
|
|
if (!bytes) return "";
|
|
if (bytes < 1024) return `${bytes} B`;
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
}
|
|
|
|
export default function MessageCard({ message, isExpanded, onToggle }: MessageCardProps) {
|
|
const isFromClient = message.author_type === "client";
|
|
const timeAgo = formatTimeAgo(message.created_at);
|
|
|
|
return (
|
|
<div
|
|
className={`
|
|
group border border-slate-200 rounded-xl transition-all duration-300 hover:shadow-md
|
|
${isFromClient
|
|
? "bg-blue-50/50 border-l-4 border-l-blue-500"
|
|
: "bg-emerald-50/50 border-l-4 border-l-emerald-500"
|
|
}
|
|
`}
|
|
>
|
|
{/* En-tête cliquable */}
|
|
<button
|
|
onClick={onToggle}
|
|
className="w-full p-4 flex items-center justify-between hover:bg-slate-50/50 rounded-xl transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-3">
|
|
{/* Avatar */}
|
|
<div
|
|
className={`
|
|
flex items-center justify-center w-10 h-10 rounded-full border-2
|
|
${isFromClient
|
|
? "bg-blue-100 border-blue-300 text-blue-600"
|
|
: "bg-emerald-100 border-emerald-300 text-emerald-600"
|
|
}
|
|
`}
|
|
>
|
|
{isFromClient ? (
|
|
<User className="w-5 h-5" />
|
|
) : (
|
|
<Settings className="w-5 h-5" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Infos */}
|
|
<div className="text-left">
|
|
<div className="flex items-center space-x-2">
|
|
<span className="font-medium text-slate-900">
|
|
{message.author_name || (isFromClient ? "Vous" : "Support Odentas")}
|
|
</span>
|
|
<span className="text-xs text-slate-500">
|
|
{timeAgo}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Aperçu du message */}
|
|
{!isExpanded && (
|
|
<div className="text-sm text-slate-600 mt-1">
|
|
<div className="line-clamp-1">
|
|
{message.content.length > 100
|
|
? `${message.content.substring(0, 100)}...`
|
|
: message.content
|
|
}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Indicateurs */}
|
|
<div className="flex items-center space-x-3 mt-1">
|
|
{message.attachments && message.attachments.length > 0 && (
|
|
<div className="flex items-center space-x-1 text-xs text-slate-500">
|
|
<Paperclip className="w-3 h-3" />
|
|
<span>{message.attachments.length} fichier{message.attachments.length > 1 ? "s" : ""}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Icône d'expansion */}
|
|
<div className="text-slate-400 group-hover:text-slate-600">
|
|
{isExpanded ? (
|
|
<ChevronDown className="w-5 h-5" />
|
|
) : (
|
|
<ChevronRight className="w-5 h-5" />
|
|
)}
|
|
</div>
|
|
</button>
|
|
|
|
{/* Contenu expandé */}
|
|
{isExpanded && (
|
|
<div className="px-4 pb-4">
|
|
{/* Divider */}
|
|
<div className="h-px bg-slate-200 mb-4" />
|
|
|
|
{/* Message complet */}
|
|
<div className="prose prose-sm max-w-none">
|
|
<div className="whitespace-pre-wrap text-slate-700 leading-relaxed">
|
|
{message.content}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Pièces jointes */}
|
|
{message.attachments && message.attachments.length > 0 && (
|
|
<div className="mt-4 space-y-2">
|
|
<div className="text-sm font-medium text-slate-600 flex items-center space-x-2">
|
|
<Paperclip className="w-4 h-4" />
|
|
<span>Pièces jointes</span>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
{message.attachments.map((attachment, index) => (
|
|
<a
|
|
key={index}
|
|
href={attachment.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center justify-between p-2 rounded-lg border border-slate-200 hover:bg-slate-50 transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<Paperclip className="w-4 h-4 text-slate-400" />
|
|
<span className="text-sm text-slate-700">
|
|
{attachment.filename}
|
|
</span>
|
|
</div>
|
|
{attachment.size && (
|
|
<span className="text-xs text-slate-500">
|
|
{formatFileSize(attachment.size)}
|
|
</span>
|
|
)}
|
|
</a>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |