analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
1 lines • 52.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/Chat/Chat.tsx","../src/hooks/useChat.ts","../src/hooks/useChatRooms.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n PaperPlaneTiltIcon,\n XIcon,\n PlusIcon,\n UsersIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport Modal from '../Modal/Modal';\nimport { SkeletonText, SkeletonRounded } from '../Skeleton/Skeleton';\nimport EmptyState from '../EmptyState/EmptyState';\nimport { useChat } from '../../hooks/useChat';\nimport { useChatRooms } from '../../hooks/useChatRooms';\nimport {\n PROFILE_ROLES,\n CHAT_MESSAGE_TYPES,\n type ChatApiClient,\n type ChatRoomWithDetails,\n type ChatMessage,\n type ChatParticipant,\n type ChatUser,\n} from '../../types/chat';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the Chat component\n */\nexport interface ChatProps {\n /** API client for REST calls */\n apiClient: ChatApiClient;\n /** WebSocket URL for real-time messaging */\n wsUrl: string;\n /** JWT authentication token */\n token: string;\n /** Current user's userInstitutionId */\n userId: string;\n /** Current user's display name */\n userName: string;\n /** Current user's profile photo URL */\n userPhoto?: string | null;\n /** Current user's role */\n userRole: PROFILE_ROLES;\n /** Additional CSS classes */\n className?: string;\n /** Initial room ID to open (from URL) */\n initialRoomId?: string;\n /** Callback when room changes (for URL navigation) */\n onRoomChange?: (roomId: string) => void;\n /** Callback when returning to room list */\n onBackToList?: () => void;\n}\n\n/**\n * Chat view states\n */\ntype ChatView = 'list' | 'room' | 'create';\n\n/**\n * Room item component for the list\n */\nconst RoomItem = ({\n room,\n onClick,\n isActive,\n}: {\n room: ChatRoomWithDetails;\n onClick: () => void;\n isActive: boolean;\n}) => (\n <Button\n variant=\"link\"\n onClick={onClick}\n className={cn(\n 'w-full p-3 rounded-lg text-left transition-colors justify-start h-auto',\n 'hover:bg-background-100',\n isActive && 'bg-primary-50 border-l-4 border-primary-500'\n )}\n >\n <div className=\"flex items-start gap-3 w-full\">\n <div className=\"w-10 h-10 rounded-full bg-primary-100 flex items-center justify-center flex-shrink-0\">\n <UsersIcon size={20} className=\"text-primary-600\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-900 truncate\">\n {room.name}\n </Text>\n {room.lastMessage && (\n <Text size=\"xs\" className=\"text-text-500 truncate mt-1\">\n {room.lastMessage.senderName}: {room.lastMessage.content}\n </Text>\n )}\n <Text size=\"xs\" className=\"text-text-400 mt-1\">\n {room.participantCount} participantes\n </Text>\n </div>\n </div>\n </Button>\n);\n\n/**\n * Message bubble component\n */\nconst MessageBubble = ({\n message,\n isOwn,\n}: {\n message: ChatMessage;\n isOwn: boolean;\n}) => (\n <div className={cn('flex gap-2 mb-3', isOwn && 'flex-row-reverse')}>\n {!isOwn && (\n <div className=\"w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center flex-shrink-0\">\n {message.senderPhoto ? (\n <img\n src={message.senderPhoto}\n alt={message.senderName}\n className=\"w-8 h-8 rounded-full object-cover\"\n />\n ) : (\n <Text size=\"xs\" weight=\"bold\" className=\"text-gray-600\">\n {message.senderName.charAt(0).toUpperCase()}\n </Text>\n )}\n </div>\n )}\n <div className={cn('max-w-[70%]', isOwn && 'items-end')}>\n {!isOwn && (\n <Text size=\"xs\" className=\"text-text-500 mb-1\">\n {message.senderName}\n </Text>\n )}\n <div\n className={cn(\n 'px-4 py-2 rounded-2xl',\n isOwn\n ? 'bg-primary-500 text-white rounded-br-md'\n : 'bg-background-100 text-text-900 rounded-bl-md'\n )}\n >\n <Text size=\"sm\">{message.content}</Text>\n </div>\n <Text\n size=\"xs\"\n className={cn('text-text-400 mt-1', isOwn && 'text-right')}\n >\n {new Date(message.createdAt).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n })}\n </Text>\n </div>\n </div>\n);\n\n/**\n * Participant item component\n */\nconst ParticipantItem = ({ participant }: { participant: ChatParticipant }) => (\n <div className=\"flex items-center gap-2 py-2\">\n <div className=\"relative\">\n <div className=\"w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center\">\n {participant.photo ? (\n <img\n src={participant.photo}\n alt={participant.name}\n className=\"w-8 h-8 rounded-full object-cover\"\n />\n ) : (\n <Text size=\"xs\" weight=\"bold\" className=\"text-gray-600\">\n {participant.name.charAt(0).toUpperCase()}\n </Text>\n )}\n </div>\n {participant.isOnline && (\n <div className=\"absolute bottom-0 right-0 w-3 h-3 bg-green-500 rounded-full border-2 border-white\" />\n )}\n </div>\n <div className=\"flex-1 min-w-0\">\n <Text size=\"sm\" className=\"text-text-900 truncate\">\n {participant.name}\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {participant.role}\n </Text>\n </div>\n </div>\n);\n\n/**\n * User selector for creating new rooms\n */\nconst UserSelector = ({\n users,\n selectedIds,\n onToggle,\n}: {\n users: ChatUser[];\n selectedIds: Set<string>;\n onToggle: (id: string) => void;\n}) => (\n <div className=\"space-y-2 max-h-64 overflow-y-auto\">\n {users.map((user) => (\n <Button\n key={user.userInstitutionId}\n variant=\"link\"\n onClick={() => onToggle(user.userInstitutionId)}\n className={cn(\n 'w-full flex items-center gap-3 p-3 rounded-lg transition-colors justify-start h-auto',\n selectedIds.has(user.userInstitutionId)\n ? 'bg-primary-50 border border-primary-500'\n : 'bg-background-50 hover:bg-background-100 border border-transparent'\n )}\n >\n <div className=\"w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center\">\n {user.photo ? (\n <img\n src={user.photo}\n alt={user.name}\n className=\"w-10 h-10 rounded-full object-cover\"\n />\n ) : (\n <Text size=\"sm\" weight=\"bold\" className=\"text-gray-600\">\n {user.name.charAt(0).toUpperCase()}\n </Text>\n )}\n </div>\n <div className=\"flex-1 text-left\">\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-900\">\n {user.name}\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {user.profileName}\n </Text>\n </div>\n <div\n className={cn(\n 'w-5 h-5 rounded-full border-2 flex items-center justify-center',\n selectedIds.has(user.userInstitutionId)\n ? 'bg-primary-500 border-primary-500'\n : 'border-gray-300'\n )}\n >\n {selectedIds.has(user.userInstitutionId) && (\n <div className=\"w-2 h-2 bg-white rounded-full\" />\n )}\n </div>\n </Button>\n ))}\n </div>\n);\n\n/**\n * Main Chat component\n *\n * Provides complete chat functionality:\n * - List of chat rooms\n * - Create new chat rooms\n * - Real-time messaging within rooms\n *\n * @example\n * ```tsx\n * <Chat\n * apiClient={api}\n * wsUrl=\"wss://api.example.com/chat/ws\"\n * token={authToken}\n * userId={user.userInstitutionId}\n * userName={user.name}\n * userPhoto={user.photo}\n * userRole=\"teacher\"\n * />\n * ```\n */\nexport function Chat(props: Readonly<ChatProps>) {\n const { userId, token } = props;\n\n // Show loading state if user info is not available\n if (!userId || !token) {\n return (\n <div className=\"flex items-center justify-center h-96\">\n <Text size=\"sm\" className=\"text-text-500\">\n Carregando...\n </Text>\n </div>\n );\n }\n\n return <ChatContent {...props} />;\n}\n\n/**\n * Internal Chat content component (rendered when user info is available)\n */\nfunction ChatContent({\n apiClient,\n wsUrl,\n token,\n userId,\n userName,\n userPhoto,\n userRole,\n className,\n initialRoomId,\n onRoomChange,\n onBackToList,\n}: Readonly<ChatProps>) {\n const [view, setView] = useState<ChatView>('list');\n const [selectedRoom, setSelectedRoom] = useState<ChatRoomWithDetails | null>(\n null\n );\n const [selectedUserIds, setSelectedUserIds] = useState<Set<string>>(\n new Set()\n );\n const [messageInput, setMessageInput] = useState('');\n const [showCreateModal, setShowCreateModal] = useState(false);\n const hasHandledInitialRoomRef = useRef(false);\n\n // Hooks for REST operations\n const {\n rooms,\n availableUsers,\n loading: roomsLoading,\n error: roomsError,\n fetchRooms,\n fetchAvailableUsers,\n createRoom,\n } = useChatRooms({ apiClient });\n\n // Hook for WebSocket (only when in a room)\n const {\n isConnected,\n messages,\n participants,\n sendMessage,\n error: chatError,\n } = useChat({\n wsUrl,\n token,\n roomId: selectedRoom?.id || '',\n userId,\n onConnect: () => console.log('Connected to chat'),\n onDisconnect: () => console.log('Disconnected from chat'),\n onError: (err) => console.error('Chat error:', err),\n });\n\n // Get role label for display\n const getRoleLabel = () => {\n return userRole === PROFILE_ROLES.TEACHER ? 'Professor' : 'Aluno';\n };\n\n // Fetch rooms on mount\n useEffect(() => {\n fetchRooms();\n }, [fetchRooms]);\n\n // Handle initial room from URL\n useEffect(() => {\n // Only handle initial room once\n if (hasHandledInitialRoomRef.current) {\n return;\n }\n\n // Wait for rooms to load before trying to find the initial room\n if (!initialRoomId || roomsLoading) {\n return;\n }\n\n // Only proceed if rooms have been loaded (not initial empty state)\n if (rooms?.length > 0) {\n const room = rooms.find((r) => r.id === initialRoomId);\n if (room) {\n hasHandledInitialRoomRef.current = true;\n setSelectedRoom(room);\n setView('room');\n } else {\n // Room not found in user's rooms, redirect to list\n hasHandledInitialRoomRef.current = true;\n onBackToList?.();\n }\n } else if (rooms !== undefined && !roomsLoading) {\n // Rooms loaded but empty, or room not found\n hasHandledInitialRoomRef.current = true;\n onBackToList?.();\n }\n }, [initialRoomId, rooms, roomsLoading, onBackToList]);\n\n // Handle room selection\n const handleSelectRoom = useCallback(\n (room: ChatRoomWithDetails) => {\n setSelectedRoom(room);\n setView('room');\n onRoomChange?.(room.id);\n },\n [onRoomChange]\n );\n\n // Handle create room\n const handleOpenCreateModal = useCallback(async () => {\n await fetchAvailableUsers();\n setSelectedUserIds(new Set());\n setShowCreateModal(true);\n }, [fetchAvailableUsers]);\n\n const handleToggleUser = useCallback((id: string) => {\n setSelectedUserIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n const handleCreateRoom = useCallback(async () => {\n if (selectedUserIds.size === 0) return;\n\n const room = await createRoom(Array.from(selectedUserIds));\n if (room) {\n setShowCreateModal(false);\n setSelectedUserIds(new Set());\n // Navigate to the new room\n onRoomChange?.(room.id);\n }\n }, [selectedUserIds, createRoom, onRoomChange]);\n\n // Handle send message\n const handleSendMessage = useCallback(() => {\n if (!messageInput.trim()) return;\n sendMessage(messageInput);\n setMessageInput('');\n }, [messageInput, sendMessage]);\n\n // Handle back to list\n const handleBackToList = useCallback(() => {\n setSelectedRoom(null);\n setView('list');\n onBackToList?.();\n }, [onBackToList]);\n\n // Render messages content\n const renderMessagesContent = () => {\n if (chatError) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-red-500 mb-2\">\n Erro de conexao com o chat\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n Tentando reconectar...\n </Text>\n </div>\n </div>\n );\n }\n\n // Filter out system messages (user joined/left notifications)\n const userMessages = messages?.filter(\n (message) => message.messageType !== CHAT_MESSAGE_TYPES.SYSTEM\n );\n\n if (!userMessages?.length) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <Text size=\"sm\" className=\"text-text-500\">\n Nenhuma mensagem ainda. Comece a conversa!\n </Text>\n </div>\n );\n }\n\n return userMessages.map((message) => (\n <MessageBubble\n key={message.id}\n message={message}\n isOwn={message.senderId === userId}\n />\n ));\n };\n\n // Render room list\n const renderRoomList = () => (\n <div className=\"flex flex-col h-full\">\n <div className=\"p-4 border-b border-background-200 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-full bg-primary-100 flex items-center justify-center flex-shrink-0\">\n {userPhoto ? (\n <img\n src={userPhoto}\n alt={userName}\n className=\"w-10 h-10 rounded-full object-cover\"\n />\n ) : (\n <Text size=\"sm\" weight=\"bold\" className=\"text-primary-600\">\n {userName.charAt(0).toUpperCase()}\n </Text>\n )}\n </div>\n <div>\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-900\">\n Conversas\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {userName} - {getRoleLabel()}\n </Text>\n </div>\n </div>\n <Button\n variant=\"solid\"\n size=\"small\"\n onClick={handleOpenCreateModal}\n iconLeft={<PlusIcon size={16} />}\n >\n Nova conversa\n </Button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-2\">\n {roomsError && (\n <div className=\"p-4 text-center\">\n <Text size=\"sm\" className=\"text-red-500 mb-2\">\n Erro ao carregar conversas\n </Text>\n <Button variant=\"outline\" size=\"small\" onClick={fetchRooms}>\n Tentar novamente\n </Button>\n </div>\n )}\n {!roomsError && roomsLoading && (\n <div className=\"space-y-3 p-2\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <SkeletonRounded className=\"w-10 h-10\" />\n <div className=\"flex-1\">\n <SkeletonText className=\"w-3/4 h-4 mb-2\" />\n <SkeletonText className=\"w-1/2 h-3\" />\n </div>\n </div>\n ))}\n </div>\n )}\n {!roomsError && !roomsLoading && !rooms?.length && (\n <EmptyState\n title=\"Nenhuma conversa\"\n description=\"Comece uma nova conversa clicando no botao acima\"\n />\n )}\n {!roomsError && !roomsLoading && !!rooms?.length && (\n <div className=\"space-y-1\">\n {rooms.map((room) => (\n <RoomItem\n key={room.id}\n room={room}\n onClick={() => handleSelectRoom(room)}\n isActive={selectedRoom?.id === room.id}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n\n // Render chat room\n const renderChatRoom = () => {\n if (!selectedRoom) return null;\n\n return (\n <div className=\"flex h-full\">\n {/* Messages area */}\n <div className=\"flex-1 flex flex-col\">\n {/* Header */}\n <div className=\"p-4 border-b border-background-200 flex items-center gap-3\">\n <Button variant=\"link\" size=\"small\" onClick={handleBackToList}>\n <XIcon size={20} />\n </Button>\n <div className=\"flex-1\">\n <Text size=\"md\" weight=\"semibold\" className=\"text-text-900\">\n {selectedRoom.name}\n </Text>\n <Text size=\"xs\" className=\"text-text-500\">\n {isConnected ? 'Conectado' : 'Conectando...'}\n </Text>\n </div>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4\">\n {renderMessagesContent()}\n </div>\n\n {/* Input */}\n <div className=\"p-4 border-t border-background-200\">\n <div className=\"flex gap-2\">\n <Input\n placeholder=\"Digite sua mensagem...\"\n value={messageInput}\n onChange={(e) => setMessageInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSendMessage();\n }\n }}\n className=\"flex-1\"\n />\n <Button\n variant=\"solid\"\n onClick={handleSendMessage}\n disabled={!messageInput.trim() || !isConnected}\n >\n <PaperPlaneTiltIcon size={20} />\n </Button>\n </div>\n </div>\n </div>\n\n {/* Participants sidebar */}\n <div className=\"w-64 border-l border-background-200 p-4 hidden lg:block\">\n <Text size=\"sm\" weight=\"semibold\" className=\"text-text-900 mb-3\">\n Participantes ({participants?.length ?? 0})\n </Text>\n <div className=\"space-y-1\">\n {participants?.map((participant) => (\n <ParticipantItem\n key={participant.userInstitutionId}\n participant={participant}\n />\n ))}\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <div\n className={cn(\n 'bg-background rounded-xl border border-background-200 h-[600px]',\n className\n )}\n >\n {view === 'list' && renderRoomList()}\n {view === 'room' && renderChatRoom()}\n\n {/* Create room modal */}\n <Modal\n isOpen={showCreateModal}\n onClose={() => setShowCreateModal(false)}\n title=\"Nova conversa\"\n >\n <div className=\"p-4\">\n <Text size=\"sm\" className=\"text-text-600 mb-4\">\n Selecione os participantes para iniciar uma conversa\n </Text>\n\n {roomsLoading && (\n <div className=\"space-y-3\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <SkeletonRounded className=\"w-10 h-10\" />\n <SkeletonText className=\"flex-1 h-4\" />\n </div>\n ))}\n </div>\n )}\n {!roomsLoading && !availableUsers?.length && (\n <Text size=\"sm\" className=\"text-text-500 text-center py-4\">\n Nenhum usuario disponivel para chat\n </Text>\n )}\n {!roomsLoading && !!availableUsers?.length && (\n <UserSelector\n users={availableUsers}\n selectedIds={selectedUserIds}\n onToggle={handleToggleUser}\n />\n )}\n\n <div className=\"flex justify-end gap-2 mt-6\">\n <Button variant=\"outline\" onClick={() => setShowCreateModal(false)}>\n Cancelar\n </Button>\n <Button\n variant=\"solid\"\n onClick={handleCreateRoom}\n disabled={selectedUserIds.size === 0 || roomsLoading}\n >\n Criar conversa\n </Button>\n </div>\n </div>\n </Modal>\n </div>\n );\n}\n\nexport default Chat;\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type {\n ChatMessage,\n ChatParticipant,\n WSServerMessage,\n WSClientMessage,\n} from '../types/chat';\n\n/**\n * WebSocket connection states\n */\nexport const WS_STATES = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Options for the useChat hook\n */\nexport interface UseChatOptions {\n /** WebSocket URL (e.g., wss://api.example.com/chat/ws) */\n wsUrl: string;\n /** JWT authentication token */\n token: string;\n /** Chat room ID */\n roomId: string;\n /** Current user's userInstitutionId */\n userId: string;\n /** Callback when connection is established */\n onConnect?: () => void;\n /** Callback when connection is closed */\n onDisconnect?: () => void;\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Reconnect interval in ms (default: 3000) */\n reconnectInterval?: number;\n /** Max reconnect attempts (default: 5) */\n maxReconnectAttempts?: number;\n}\n\n/**\n * Return type for the useChat hook\n */\nexport interface UseChatReturn {\n /** Whether WebSocket is connected */\n isConnected: boolean;\n /** List of chat messages */\n messages: ChatMessage[];\n /** List of room participants */\n participants: ChatParticipant[];\n /** Send a text message */\n sendMessage: (content: string) => void;\n /** Leave the chat room */\n leave: () => void;\n /** Current error state */\n error: Error | null;\n /** Reconnect to the WebSocket */\n reconnect: () => void;\n /** Current user ID for identifying own messages */\n currentUserId: string;\n}\n\n/**\n * Hook for managing WebSocket connection to a chat room\n *\n * Handles:\n * - WebSocket connection lifecycle\n * - Message sending/receiving\n * - Participant tracking\n * - Auto-reconnection\n *\n * @param options - Hook configuration options\n * @returns Chat state and actions\n *\n * @example\n * ```tsx\n * const {\n * isConnected,\n * messages,\n * participants,\n * sendMessage,\n * error\n * } = useChat({\n * wsUrl: 'wss://api.example.com/chat/ws',\n * token: authToken,\n * roomId: 'room-123',\n * userId: 'user-456',\n * onConnect: () => console.log('Connected!'),\n * onError: (err) => console.error('Error:', err),\n * });\n *\n * const handleSend = () => {\n * sendMessage('Hello, world!');\n * };\n * ```\n */\nexport function useChat({\n wsUrl,\n token,\n roomId,\n userId,\n onConnect,\n onDisconnect,\n onError,\n autoReconnect = true,\n reconnectInterval = 3000,\n maxReconnectAttempts = 5,\n}: UseChatOptions): UseChatReturn {\n const [isConnected, setIsConnected] = useState(false);\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [participants, setParticipants] = useState<ChatParticipant[]>([]);\n const [error, setError] = useState<Error | null>(null);\n\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectAttemptsRef = useRef(0);\n const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const isManualDisconnectRef = useRef(false);\n const isConnectingRef = useRef(false);\n const connectRef = useRef<() => void>(() => {});\n\n /**\n * Send a WebSocket message\n */\n const sendWsMessage = useCallback((message: WSClientMessage) => {\n if (wsRef.current?.readyState === WS_STATES.OPEN) {\n wsRef.current.send(JSON.stringify(message));\n }\n }, []);\n\n /**\n * Send a text message\n */\n const sendMessage = useCallback(\n (content: string) => {\n const trimmedContent = content.trim();\n if (!trimmedContent) return;\n\n sendWsMessage({\n type: 'message',\n payload: { content: trimmedContent },\n });\n },\n [sendWsMessage]\n );\n\n /**\n * Leave the chat room\n */\n const leave = useCallback(() => {\n isManualDisconnectRef.current = true;\n sendWsMessage({ type: 'leave' });\n wsRef.current?.close(1000, 'User left');\n }, [sendWsMessage]);\n\n /**\n * Handle incoming WebSocket messages\n */\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data) as WSServerMessage;\n\n switch (data.type) {\n case 'history':\n if (data.payload.messages) {\n setMessages(data.payload.messages);\n }\n break;\n\n case 'participants':\n if (data.payload.participants) {\n setParticipants(data.payload.participants);\n }\n break;\n\n case 'new_message':\n if (data.payload.message) {\n setMessages((prev) => [...prev, data.payload.message!]);\n }\n break;\n\n case 'user_joined':\n if (data.payload.user) {\n const user = data.payload.user;\n setParticipants((prev) => {\n // Check if user already exists\n const exists = prev.some(\n (p) => p.userInstitutionId === user.userInstitutionId\n );\n if (exists) {\n // Update online status\n return prev.map((p) =>\n p.userInstitutionId === user.userInstitutionId\n ? { ...p, isOnline: true }\n : p\n );\n }\n // Add new participant\n return [\n ...prev,\n {\n userInstitutionId: user.userInstitutionId,\n name: user.name,\n photo: user.photo,\n role: user.role,\n isOnline: true,\n },\n ];\n });\n }\n break;\n\n case 'user_left':\n if (data.payload.user) {\n const user = data.payload.user;\n setParticipants((prev) =>\n prev.map((p) =>\n p.userInstitutionId === user.userInstitutionId\n ? { ...p, isOnline: false }\n : p\n )\n );\n }\n break;\n\n case 'error': {\n const errorMessage =\n data.payload.message_text || 'Erro desconhecido';\n setError(new Error(errorMessage));\n onError?.(new Error(errorMessage));\n break;\n }\n }\n } catch (err) {\n console.error('Error parsing WebSocket message:', err);\n }\n },\n [onError]\n );\n\n /**\n * Connect to WebSocket\n */\n const connect = useCallback(() => {\n // Prevent multiple simultaneous connection attempts\n if (isConnectingRef.current) {\n return;\n }\n\n // Build WebSocket URL with query params\n const url = new URL(wsUrl);\n url.searchParams.set('roomId', roomId);\n url.searchParams.set('token', token);\n\n // Close existing connection\n if (wsRef.current) {\n wsRef.current.close();\n }\n\n // Create new WebSocket connection\n isConnectingRef.current = true;\n const ws = new WebSocket(url.toString());\n wsRef.current = ws;\n\n ws.onopen = () => {\n isConnectingRef.current = false;\n setIsConnected(true);\n setError(null);\n reconnectAttemptsRef.current = 0;\n ws.send(JSON.stringify({ type: 'getInitialData' }));\n\n onConnect?.();\n };\n\n ws.onmessage = handleMessage;\n\n ws.onerror = () => {\n isConnectingRef.current = false;\n const error = new Error('Erro na conexão WebSocket');\n setError(error);\n onError?.(error);\n };\n\n ws.onclose = (event) => {\n isConnectingRef.current = false;\n setIsConnected(false);\n onDisconnect?.();\n\n // Auto-reconnect if not manual disconnect and within attempts limit\n if (\n autoReconnect &&\n !isManualDisconnectRef.current &&\n reconnectAttemptsRef.current < maxReconnectAttempts &&\n event.code !== 4001 && // Unauthorized\n event.code !== 4002 && // Missing roomId\n event.code !== 4003 // Access denied\n ) {\n reconnectAttemptsRef.current += 1;\n reconnectTimeoutRef.current = setTimeout(() => {\n connect();\n }, reconnectInterval);\n }\n };\n }, [\n wsUrl,\n roomId,\n token,\n handleMessage,\n onConnect,\n onDisconnect,\n onError,\n autoReconnect,\n reconnectInterval,\n maxReconnectAttempts,\n ]);\n\n // Keep connect ref updated\n connectRef.current = connect;\n\n /**\n * Reconnect to WebSocket\n */\n const reconnect = useCallback(() => {\n isManualDisconnectRef.current = false;\n reconnectAttemptsRef.current = 0;\n connectRef.current();\n }, []);\n\n // Connect on mount, disconnect on unmount\n // Only connect when roomId is provided\n useEffect(() => {\n if (!roomId) {\n return;\n }\n\n // Clear any pending reconnect\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n\n isManualDisconnectRef.current = false;\n isConnectingRef.current = false;\n reconnectAttemptsRef.current = 0;\n\n // Close existing connection before opening new one\n if (wsRef.current) {\n wsRef.current.close(1000, 'Room changed');\n wsRef.current = null;\n }\n\n // Small delay to ensure previous connection is closed\n const timeoutId = setTimeout(() => {\n connectRef.current();\n }, 100);\n\n return () => {\n clearTimeout(timeoutId);\n isManualDisconnectRef.current = true;\n isConnectingRef.current = false;\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n }\n wsRef.current?.close(1000, 'Component unmounted');\n };\n }, [roomId]);\n\n return {\n isConnected,\n messages,\n participants,\n sendMessage,\n leave,\n error,\n reconnect,\n currentUserId: userId,\n };\n}\n\n/**\n * Factory function to create a pre-configured useChat hook\n *\n * @param baseWsUrl - Base WebSocket URL\n * @returns Factory function that creates the hook with room-specific options\n *\n * @example\n * ```tsx\n * // In your app setup\n * const createChatHook = createUseChat('wss://api.example.com/chat/ws');\n *\n * // In component\n * const { messages, sendMessage } = createChatHook({\n * token: authToken,\n * roomId: 'room-123',\n * userId: 'user-456',\n * });\n * ```\n */\nexport function createUseChat(baseWsUrl: string) {\n return (options: Omit<UseChatOptions, 'wsUrl'>) =>\n useChat({ ...options, wsUrl: baseWsUrl });\n}\n","import { useState, useCallback } from 'react';\nimport type {\n ChatApiClient,\n ChatRoom,\n ChatRoomWithDetails,\n ChatUser,\n AvailableUsersResponse,\n CreateRoomResponse,\n GetRoomsResponse,\n} from '../types/chat';\n\n/**\n * Options for the useChatRooms hook\n */\nexport interface UseChatRoomsOptions {\n apiClient: ChatApiClient;\n}\n\n/**\n * Return type for the useChatRooms hook\n */\nexport interface UseChatRoomsReturn {\n /** List of user's chat rooms */\n rooms: ChatRoomWithDetails[];\n /** Available users for creating new chat rooms */\n availableUsers: ChatUser[];\n /** Loading state */\n loading: boolean;\n /** Error state */\n error: Error | null;\n /** Fetch user's chat rooms */\n fetchRooms: () => Promise<void>;\n /** Fetch available users for chat */\n fetchAvailableUsers: () => Promise<void>;\n /** Create a new chat room */\n createRoom: (participantIds: string[]) => Promise<ChatRoom | null>;\n /** Clear error state */\n clearError: () => void;\n}\n\n/**\n * Hook for managing chat rooms via REST API\n *\n * Provides functionality to:\n * - List user's chat rooms\n * - Fetch available users for new chats\n * - Create new chat rooms\n *\n * @param options - Hook configuration options\n * @returns Chat rooms state and actions\n *\n * @example\n * ```tsx\n * const { rooms, fetchRooms, createRoom } = useChatRooms({ apiClient: api });\n *\n * useEffect(() => {\n * fetchRooms();\n * }, []);\n *\n * const handleCreateRoom = async (userIds: string[]) => {\n * const room = await createRoom(userIds);\n * if (room) {\n * // Navigate to the new room\n * }\n * };\n * ```\n */\nexport function useChatRooms({\n apiClient,\n}: UseChatRoomsOptions): UseChatRoomsReturn {\n const [rooms, setRooms] = useState<ChatRoomWithDetails[]>([]);\n const [availableUsers, setAvailableUsers] = useState<ChatUser[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n /**\n * Fetch user's chat rooms\n */\n const fetchRooms = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const response = await apiClient.get<{ data: GetRoomsResponse }>(\n '/chat/rooms'\n );\n setRooms(response.data.data.rooms);\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Erro ao carregar salas');\n setError(error);\n console.error('Error fetching chat rooms:', err);\n } finally {\n setLoading(false);\n }\n }, [apiClient]);\n\n /**\n * Fetch available users for chat\n */\n const fetchAvailableUsers = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const response = await apiClient.get<{ data: AvailableUsersResponse }>(\n '/chat/available-users'\n );\n setAvailableUsers(response.data.data.users);\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Erro ao carregar usuários');\n setError(error);\n console.error('Error fetching available users:', err);\n } finally {\n setLoading(false);\n }\n }, [apiClient]);\n\n /**\n * Create a new chat room\n */\n const createRoom = useCallback(\n async (participantIds: string[]): Promise<ChatRoom | null> => {\n setLoading(true);\n setError(null);\n\n try {\n const response = await apiClient.post<{ data: CreateRoomResponse }>(\n '/chat/rooms',\n {\n participantIds,\n }\n );\n\n // Refresh rooms list after creating\n await fetchRooms();\n\n return response.data.data.room;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Erro ao criar sala');\n setError(error);\n console.error('Error creating chat room:', err);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apiClient, fetchRooms]\n );\n\n /**\n * Clear error state\n */\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n rooms,\n availableUsers,\n loading,\n error,\n fetchRooms,\n fetchAvailableUsers,\n createRoom,\n clearError,\n };\n}\n\n/**\n * Factory function to create a pre-configured useChatRooms hook\n *\n * @param apiClient - API client instance\n * @returns Pre-configured hook function\n *\n * @example\n * ```tsx\n * // In your app setup\n * import { createUseChatRooms } from 'analytica-frontend-lib';\n * import api from './services/api';\n *\n * export const useChatRooms = createUseChatRooms(api);\n *\n * // Then use directly in components\n * const { rooms, fetchRooms } = useChatRooms();\n * ```\n */\nexport function createUseChatRooms(apiClient: ChatApiClient) {\n return () => useChatRooms({ apiClient });\n}\n"],"mappings":"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