Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions src/components/chat/ChatSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,19 @@ export function ChatSidebar({ currentRoomId, onRoomChange, onUserDoubleClick, cl
// Buscar todos os colaboradores
const {
data: usersData,
isLoading: isLoadingUsers
isLoading: isLoadingUsers,
error: usersError
} = api.user.listForChat.useQuery({
search: searchTerm || undefined,
search: searchTerm.trim() || undefined,
}, {
enabled: activeTab === "colaboradores"
})

// Log de erro se houver
if (usersError) {
console.error('Erro ao buscar usuários:', usersError)
}

// Buscar status de presença online
const { data: presenceData } = useQuery({
queryKey: ['online-users'],
Expand Down Expand Up @@ -116,19 +122,32 @@ export function ChatSidebar({ currentRoomId, onRoomChange, onUserDoubleClick, cl
}, {} as Record<string, User[]>)

// Filtrar colaboradores por busca
const filteredCollaborators = searchTerm.trim()
? allColaborators.filter((user: User) =>
formatUserName(user).toLowerCase().includes(searchTerm.toLowerCase()) ??
user.email.toLowerCase().includes(searchTerm.toLowerCase()) ??
user.setor?.toLowerCase().includes(searchTerm.toLowerCase()) ??
user.enterprise.toLowerCase().includes(searchTerm.toLowerCase())
)
: allColaborators
const filteredCollaborators = (() => {
try {
const trimmedSearch = searchTerm.trim().toLowerCase()
if (!trimmedSearch) return allColaborators

return allColaborators.filter((user: User) => {
const userName = formatUserName(user).toLowerCase()
const email = user.email.toLowerCase()
const setor = user.setor?.toLowerCase() ?? ''
const enterprise = user.enterprise.toLowerCase()

return userName.includes(trimmedSearch) ||
email.includes(trimmedSearch) ||
setor.includes(trimmedSearch) ||
enterprise.includes(trimmedSearch)
})
} catch (error) {
console.error('Erro ao filtrar colaboradores:', error)
return allColaborators
}
})()

const activeConversations = (activeConversationsData ?? []) as Array<{
roomId: string
roomName: string
roomType: 'global' | 'group'
roomType: 'global' | 'group' | 'private'
lastMessage: {
content: string | null
createdAt: Date
Expand Down Expand Up @@ -268,6 +287,8 @@ export function ChatSidebar({ currentRoomId, onRoomChange, onUserDoubleClick, cl
<div className="flex-shrink-0">
{conversation.roomType === 'global' ? (
<Hash className="h-4 w-4 text-muted-foreground" />
) : conversation.roomType === 'private' ? (
<MessageCircle className="h-4 w-4 text-muted-foreground" />
) : (
<Users className="h-4 w-4 text-muted-foreground" />
)}
Expand Down Expand Up @@ -394,8 +415,18 @@ export function ChatSidebar({ currentRoomId, onRoomChange, onUserDoubleClick, cl
<Input
placeholder="Buscar colaborador..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onChange={(e) => {
try {
// Limitar tamanho e remover caracteres potencialmente problemáticos
const value = e.target.value.slice(0, 100).replace(/[<>]/g, '')
setSearchTerm(value)
} catch (error) {
console.error('Erro ao atualizar busca:', error)
setSearchTerm('')
}
}}
className="pl-9 h-9"
maxLength={100}
/>
</div>
</div>
Expand All @@ -412,6 +443,14 @@ export function ChatSidebar({ currentRoomId, onRoomChange, onUserDoubleClick, cl
</div>
))}
</div>
) : usersError ? (
<div className="text-center py-8 px-4">
<div className="text-destructive mb-3">⚠️</div>
<h4 className="text-sm font-medium mb-2">Erro ao carregar colaboradores</h4>
<p className="text-xs text-muted-foreground">
Não foi possível carregar a lista de colaboradores.
</p>
</div>
) : filteredCollaborators.length === 0 ? (
<div className="text-center py-8 px-4">
<Users className="h-12 w-12 text-muted-foreground mx-auto mb-3" />
Expand Down
82 changes: 81 additions & 1 deletion src/server/api/routers/chat-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export const chatMessageRouter = createTRPCRouter({
const conversations: Array<{
roomId: string
roomName: string
roomType: 'global' | 'group'
roomType: 'global' | 'group' | 'private'
lastMessage: {
content: string | null
createdAt: Date
Expand Down Expand Up @@ -297,6 +297,86 @@ export const chatMessageRouter = createTRPCRouter({
})
}

// Buscar chats privados com mensagens recentes
// Buscar todas as mensagens privadas distintas por roomId
const privateRoomIds = await ctx.db.chat_message.findMany({
where: {
roomId: {
startsWith: 'private_',
},
OR: [
{ userId }, // Mensagens enviadas pelo usuário atual
{
roomId: {
contains: `user_${userId}`, // RoomIds que contenham o ID do usuário atual
}
}
]
},
select: {
roomId: true,
},
distinct: ['roomId'],
})

// Para cada roomId único, buscar a última mensagem
const uniquePrivateRoomIds = [...new Set(privateRoomIds.map(msg => msg.roomId))]

for (const roomId of uniquePrivateRoomIds) {
const lastMessage = await ctx.db.chat_message.findFirst({
where: { roomId },
orderBy: { createdAt: 'desc' },
include: {
user: {
select: {
firstName: true,
lastName: true,
id: true,
},
},
},
})

if (lastMessage) {
// Extrair o ID do outro usuário do roomId
const idPattern = /user_[^_]+/g
const matches = roomId.match(idPattern) ?? []
const otherUserId = matches.find(id => id !== `user_${userId}`)?.replace('user_', '')

if (otherUserId) {
// Buscar informações do outro usuário
const otherUser = await ctx.db.user.findUnique({
where: { id: otherUserId },
select: {
firstName: true,
lastName: true,
email: true,
},
})

if (otherUser) {
const otherUserName = [otherUser.firstName, otherUser.lastName]
.filter(Boolean)
.join(' ') || otherUser.email

conversations.push({
roomId,
roomName: otherUserName,
roomType: 'private' as const,
lastMessage: {
content: lastMessage.content,
createdAt: lastMessage.createdAt,
user: {
firstName: lastMessage.user.firstName,
lastName: lastMessage.user.lastName,
},
},
})
}
}
}
}

// Filtrar apenas conversas que têm mensagens e ordenar por data da última mensagem
return conversations
.filter(conv => conv.lastMessage !== null)
Expand Down
Loading