99 lines
No EOL
3.2 KiB
TypeScript
99 lines
No EOL
3.2 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { useChatStore } from '../store/chatStore';
|
|
import { useAuthStore } from '@/features/auth/store/authStore';
|
|
import { apiClient } from '@/lib/apiClient';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { cn } from '@/lib/utils';
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
interface ConversationItemProps {
|
|
conversation: { id: string; name: string; type: string };
|
|
onSelect: (id: string) => void;
|
|
isSelected: boolean;
|
|
}
|
|
|
|
const ConversationItem: React.FC<ConversationItemProps> = ({ conversation, onSelect, isSelected }) => {
|
|
return (
|
|
<div
|
|
onClick={() => onSelect(conversation.id)}
|
|
className={cn(
|
|
"flex items-center p-3 rounded-lg cursor-pointer transition-colors",
|
|
isSelected ? "bg-blue-100 text-blue-800" : "hover:bg-gray-100"
|
|
)}
|
|
>
|
|
<span className="font-medium">{conversation.name || `Conversation ${conversation.id.substring(0, 8)}`}</span>
|
|
{/* <span className="ml-auto text-sm opacity-70">3</span> */}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const ChatSidebar: React.FC = () => {
|
|
const { userId } = useAuthStore();
|
|
const { conversations, currentConversationId, setCurrentConversation, addConversation } = useChatStore();
|
|
|
|
// Fetch conversations from backend
|
|
const { data, isLoading, error } = useQuery({
|
|
queryKey: ['chatConversations', userId],
|
|
queryFn: async () => {
|
|
if (!userId) return [];
|
|
const response = await apiClient.get('/conversations');
|
|
return response.data.conversations;
|
|
},
|
|
enabled: !!userId,
|
|
onSuccess: (data) => {
|
|
data.forEach((conv: any) => addConversation({
|
|
id: conv.id,
|
|
name: conv.name,
|
|
type: conv.type,
|
|
participants: conv.participants,
|
|
unread_count: 0 // Default for now
|
|
}));
|
|
}
|
|
});
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="w-64 border-r bg-gray-50 flex items-center justify-center">
|
|
<Loader2 className="animate-spin text-blue-500" size={24} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="w-64 border-r bg-gray-50 flex items-center justify-center text-red-500 p-4">
|
|
Erreur: {(error as any).message || "Impossible de charger les conversations"}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-64 border-r bg-gray-50 flex flex-col">
|
|
<div className="p-4 border-b">
|
|
<h2 className="text-xl font-bold">Conversations</h2>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto p-2">
|
|
{conversations.length === 0 ? (
|
|
<div className="text-gray-500 text-sm p-2">
|
|
Aucune conversation. Créez-en une !
|
|
</div>
|
|
) : (
|
|
conversations.map((conv) => (
|
|
<ConversationItem
|
|
key={conv.id}
|
|
conversation={conv}
|
|
onSelect={setCurrentConversation}
|
|
isSelected={conv.id === currentConversationId}
|
|
/>
|
|
))
|
|
)}
|
|
</div>
|
|
<div className="p-4 border-t">
|
|
{/* Button to create new conversation (TODO) */}
|
|
<button className="w-full p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
Nouvelle Conversation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |