import { FC, ReactElement, useState, useRef, useEffect, useMemo } from 'react'; import { Button } from '@imphnen-frontend-service/ui/atoms'; import { useNavigate, useParams } from 'react-router'; import { useTeamById, useTeamMessages, useSendMessage, useDeleteMessage, useAuthStore, } from '@imphnen-frontend-service/service'; import { toast } from 'sonner'; import { Icon } from '@iconify/react'; const TeamChatPage: FC = (): ReactElement => { const { teamId } = useParams<{ teamId: string }>(); const navigate = useNavigate(); const { session } = useAuthStore(); const [message, setMessage] = useState(''); const messagesEndRef = useRef(null); const { data: teamData } = useTeamById(teamId || ''); const { data: messages, isLoading } = useTeamMessages(teamId || ''); const { mutateAsync: sendMessage, isPending: isSending } = useSendMessage( teamId || '' ); const { mutateAsync: deleteMessage } = useDeleteMessage(teamId || ''); const team = teamData?.data; const currentUserId = session?.user?.id; // Message type used for UI rendering interface ChatMessage { id: string; user_id: string; user?: { avatar?: string; fullname?: string; }; message: string; created_at: string; } // Inline delete confirmation UI state const [deleteTargetId, setDeleteTargetId] = useState(null); // Prepare messages: de-duplicate by id and sort by created_at const displayMessages = useMemo(() => { const raw: ChatMessage[] = Array.isArray(messages) ? (messages as ChatMessage[]) : []; const seen = new Set(); const dedup: ChatMessage[] = []; for (const m of raw) { const key = m.id ?? `${m.user_id}-${m.created_at}`; if (!seen.has(key)) { seen.add(key); dedup.push(m); } } dedup.sort( (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime() ); return dedup; }, [messages]); // Auto-scroll to bottom when new messages arrive useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [displayMessages]); const handleSendMessage = async (e: React.FormEvent) => { e.preventDefault(); if (!message.trim() || isSending) return; try { await sendMessage(message.trim()); setMessage(''); } catch (error) { console.error('Failed to send message:', error); toast.error('Failed to send message'); } }; const handleDeleteMessage = async (messageId: string) => { try { await deleteMessage(messageId); toast.success('Message deleted'); setDeleteTargetId(null); } catch (error) { console.error('Failed to delete message:', error); toast.error('Failed to delete message'); } }; const formatTime = (timestamp: string) => { const date = new Date(timestamp); const now = new Date(); const diffInMs = now.getTime() - date.getTime(); const diffInMins = Math.floor(diffInMs / 60000); if (diffInMins < 1) return 'Just now'; if (diffInMins < 60) return `${diffInMins}m ago`; if (diffInMins < 1440) return `${Math.floor(diffInMins / 60)}h ago`; return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); }; return (
{/* Header */}

Team Chat

{team?.name}

{/* Messages Container */}
{isLoading ? (
) : displayMessages && displayMessages.length > 0 ? (
{displayMessages.map((msg) => { const isOwnMessage = msg.user_id === currentUserId; const isLeader = team?.leader_id === currentUserId; const canDelete = isOwnMessage || isLeader; return (
{/* Avatar */}
{msg.user?.avatar ? ( {msg.user.fullname} ) : (
{msg.user?.fullname?.charAt(0) || '?'}
)}
{/* Message Bubble */}
{isOwnMessage ? 'You' : msg.user?.fullname} {formatTime(msg.created_at)}

{msg.message}

{/* Delete button */} {canDelete && ( )}
{canDelete && deleteTargetId === msg.id && (
)}
); })}
) : (

No messages yet

Be the first to start the conversation!

)}
{/* Message Input */}
setMessage(e.target.value)} placeholder="Type your message..." className="flex-1 px-4 py-3 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-primary-500 focus:border-transparent" disabled={isSending} />
); }; export default TeamChatPage;