import { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '@/hooks/useAuth'; import { Card } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { PageHeader } from '@/components/ui/page-header'; import { MessageCircle, Search, Phone, Video, MoreVertical, Send, Paperclip, Mic, Users, Settings, UserPlus, Briefcase } from 'lucide-react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiRequest } from '@/lib/queryClient'; interface Contact { id: number; name: string; avatar?: string; lastMessage?: string; lastMessageTime?: string; unreadCount?: number; isOnline?: boolean; isProfessional?: boolean; professionalType?: string; } interface Message { id: number; content: string; senderId: number; timestamp: string; type: 'text' | 'image' | 'document' | 'voice'; isEdited?: boolean; status: 'sent' | 'delivered' | 'read'; } interface Chat { id: number; name?: string; type: 'direct' | 'group' | 'professional_service'; participants: any[]; messages: Message[]; } export default function ChatPage() { const { t } = useTranslation(); const { user } = useAuth(); const queryClient = useQueryClient(); const [selectedChat, setSelectedChat] = useState(null); const [newMessage, setNewMessage] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const messagesEndRef = useRef(null); const [ws, setWs] = useState(null); // Fetch contacts/chats const { data: chats = [], isLoading } = useQuery({ queryKey: ['/api/chats'], enabled: !!user, }); // Fetch messages for selected chat const { data: messages = [] } = useQuery({ queryKey: ['/api/chats', selectedChat, 'messages'], enabled: !!selectedChat, }); // Fetch professional services const { data: professionalServices = [] } = useQuery({ queryKey: ['/api/professional-services'], }); // Send message mutation const sendMessageMutation = useMutation({ mutationFn: async (messageData: { chatId: number; content: string; type: string }) => { return apiRequest(`/api/chats/${messageData.chatId}/messages`, { method: 'POST', body: JSON.stringify(messageData), }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/chats', selectedChat, 'messages'] }); setNewMessage(''); }, }); // WebSocket connection for real-time messaging useEffect(() => { if (!user) return; const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/ws`; const websocket = new WebSocket(wsUrl); websocket.onopen = () => { console.log('Connected to chat WebSocket'); websocket.send(JSON.stringify({ type: 'join', userId: user.id })); }; websocket.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'new_message') { queryClient.invalidateQueries({ queryKey: ['/api/chats'] }); if (data.chatId === selectedChat) { queryClient.invalidateQueries({ queryKey: ['/api/chats', selectedChat, 'messages'] }); } } }; websocket.onclose = () => { console.log('Chat WebSocket disconnected'); }; setWs(websocket); return () => { websocket.close(); }; }, [user, selectedChat, queryClient]); // Auto-scroll to bottom when new messages arrive useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const handleSendMessage = () => { if (!newMessage.trim() || !selectedChat) return; sendMessageMutation.mutate({ chatId: selectedChat, content: newMessage.trim(), type: 'text' }); }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSendMessage(); } }; const filteredChats = chats.filter((chat: any) => chat.name?.toLowerCase().includes(searchQuery.toLowerCase()) || chat.participants?.some((p: any) => p.name?.toLowerCase().includes(searchQuery.toLowerCase()) ) ); const formatTime = (timestamp: string) => { return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; const formatDate = (timestamp: string) => { const date = new Date(timestamp); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); if (date.toDateString() === today.toDateString()) { return 'Today'; } else if (date.toDateString() === yesterday.toDateString()) { return 'Yesterday'; } else { return date.toLocaleDateString(); } }; if (isLoading) { return (
{t('common.loading')}
); } return (
{/* Sidebar - Chat List */}
{/* Header */}

{t('chat.title')}

{/* Search */}
setSearchQuery(e.target.value)} className="pl-10" />
{/* Professional Services Section */}
{t('chat.professionalServices')}
{professionalServices.slice(0, 4).map((service: any) => (
{service.name?.[0]}
{service.name}
{service.type}
))}
{/* Chat List */}
{filteredChats.map((chat: any) => (
setSelectedChat(chat.id)} className={`p-3 rounded-lg cursor-pointer mb-1 transition-colors ${ selectedChat === chat.id ? 'bg-primary/10 border border-primary/20' : 'hover:bg-accent' }`} >
{chat.name?.[0] || chat.participants?.[0]?.name?.[0]} {chat.isOnline && (
)}

{chat.name || chat.participants?.[0]?.name} {chat.isProfessional && ( {chat.professionalType} )}

{formatTime(chat.lastMessageAt)}

{chat.lastMessage || t('chat.noMessages')}

{chat.unreadCount > 0 && ( {chat.unreadCount} )}
))}
{/* Main Chat Area */}
{selectedChat ? ( <> {/* Chat Header */}
US

{t('chat.selectedChat')}

{t('chat.lastSeen')} 2 {t('chat.minutesAgo')}

{/* Messages Area */}
{messages.map((message: Message, index: number) => { const isFromUser = message.senderId === user?.id; const showDate = index === 0 || formatDate(message.timestamp) !== formatDate(messages[index - 1]?.timestamp); return (
{showDate && (
{formatDate(message.timestamp)}
)}
{!isFromUser && ( U )}

{message.content}

{formatTime(message.timestamp)} {message.isEdited && ( {t('chat.edited')} )} {isFromUser && (
{message.status === 'read' ? '✓✓' : '✓'}
)}
); })}
{/* Message Input */}
setNewMessage(e.target.value)} onKeyPress={handleKeyPress} className="pr-12" />
) : ( /* Welcome Screen */

{t('chat.welcome')}

{t('chat.welcomeDescription')}

)}
); }