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
6 changes: 5 additions & 1 deletion src/app/(authenticated)/chat/_components/users-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ export function UsersList({ onUserDoubleClick, className }: UsersListProps) {
// Estados computados
const onlineUserIds = new Set(presenceData?.onlineUserIds ?? [])
const allUsers = usersData ?? []
const allColaborators = allUsers.filter((user: User) => user.setor !== 'Sistema')
const allColaborators = allUsers.filter((user: User) =>
user.setor !== 'Sistema' &&
user.setor !== 'Sem setor' &&
user.setor !== 'TESTE'
)

const usersBySector = allColaborators.reduce((acc: Record<string, User[]>, user: User) => {
const sector = user.setor ?? 'Sem Setor'
Expand Down
44 changes: 23 additions & 21 deletions src/components/chat/ChatRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,7 @@ export function ChatRoom({ roomId = "global", className }: ChatRoomProps) {
{/* Área de Mensagens */}
<div
ref={messagesContainerRef}
className="flex-1 p-3 overflow-y-auto"
style={{ maxHeight: '60vh', minHeight: '200px' }}
className="flex-1 p-3 overflow-y-auto min-h-0"
onScroll={handleScroll}
>
{/* Indicador de carregamento no topo */}
Expand Down Expand Up @@ -369,43 +368,46 @@ export function ChatRoom({ roomId = "global", className }: ChatRoomProps) {
</div>
</div>

{/* Formulário de Envio - Fixo na parte inferior */}
<div className="border-t bg-background flex-shrink-0">
<form onSubmit={sendMessage} className="p-3">
{/* Formulário de Envio - Sempre na parte inferior */}
<div className="border-t bg-background/95 backdrop-blur-sm flex-shrink-0 mt-auto">
<form onSubmit={sendMessage} className="p-4 space-y-3">
{/* Preview da imagem selecionada */}
{selectedImageUrl && (
<div className="mb-3">
<div className="flex justify-center">
<ImageUpload
onImageUploaded={() => undefined} // Já foi feito no onImageUploaded
onRemove={() => setSelectedImageUrl(null)}
className="mb-2"
className="max-w-xs"
/>
</div>
)}

<div className="flex gap-2">
<Input
value={inputMessage}
onChange={(e) => {
setInputMessage(e.target.value)
handleTyping()
}}
placeholder="Digite sua mensagem..."
className="flex-1"
maxLength={2000}
disabled={!isConnected}
/>
<div className="flex gap-3 items-end">
<div className="flex-1">
<Input
value={inputMessage}
onChange={(e) => {
setInputMessage(e.target.value)
handleTyping()
}}
placeholder="Digite sua mensagem..."
className="min-h-[44px] resize-none"
maxLength={2000}
disabled={!isConnected}
/>
</div>

<ImageUpload
onImageUploaded={setSelectedImageUrl}
disabled={!isConnected}
className="flex-shrink-0"
className="flex-shrink-0 mb-1"
/>

<Button
type="submit"
size="sm"
size="default"
disabled={(!inputMessage.trim() && !selectedImageUrl) || !isConnected}
className="h-[44px] px-4"
>
<Send className="h-4 w-4" />
</Button>
Expand Down
179 changes: 137 additions & 42 deletions src/components/chat/ChatSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export function ChatSidebar({ currentRoomId, onRoomChange, className }: ChatSide
{ enabled: !!clerkUser?.id }
)

// Buscar conversas ativas
const activeConversationsQuery = api.chatMessage.getActiveConversations.useQuery(
undefined,
{ enabled: !!clerkUser?.id }
)

// Chat global sempre disponível
const globalRoom: Room = {
id: 'global',
Expand All @@ -67,60 +73,149 @@ export function ChatSidebar({ currentRoomId, onRoomChange, className }: ChatSide
allRooms.push(...groupRooms)
}

const activeConversations = activeConversationsQuery.data ?? []
const isLoading = userGroupsQuery.isLoading || activeConversationsQuery.isLoading

return (
<div className={cn("w-64 border-r bg-muted/30", className)}>
<div className="p-4 border-b">
<div className={cn("w-64 border-r bg-muted/30 flex flex-col", className)}>
{/* Header */}
<div className="p-4 border-b flex-shrink-0">
<div className="flex items-center justify-between">
<h2 className="font-semibold">Salas</h2>
</div>
</div>

<div className="p-2">
{userGroupsQuery.isLoading ? (
<div className="space-y-2">
{[1, 2, 3].map((i) => (
<div key={i} className="h-16 bg-muted rounded animate-pulse"></div>
))}
</div>
) : (
<div className="space-y-1">
{allRooms.map((room) => (
<Button
key={room.id}
variant={currentRoomId === room.id ? "secondary" : "ghost"}
className={cn(
"w-full justify-start h-auto p-3",
currentRoomId === room.id && "bg-primary/10"
)}
onClick={() => onRoomChange(room.id)}
>
<div className="flex items-center gap-3 w-full">
<div className="flex-shrink-0">
{room.type === 'global' ? (
<Hash className="h-4 w-4 text-muted-foreground" />
) : (
<Users className="h-4 w-4 text-muted-foreground" />
)}
</div>
{/* Conteúdo rolável */}
<div className="flex-1 overflow-y-auto">
{/* Conversas Ativas */}
{activeConversations.length > 0 && (
<div className="p-2 border-b border-border/50">
<div className="px-2 py-1">
<h3 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
Conversas Ativas
</h3>
</div>
<div className="space-y-1">
{activeConversations.map((conversation) => (
<Button
key={conversation.roomId}
variant={currentRoomId === conversation.roomId ? "secondary" : "ghost"}
className={cn(
"w-full justify-start h-auto p-3",
currentRoomId === conversation.roomId && "bg-primary/10"
)}
onClick={() => onRoomChange(conversation.roomId)}
>
<div className="flex items-center gap-3 w-full">
<div className="flex-shrink-0">
{conversation.roomType === 'global' ? (
<Hash className="h-4 w-4 text-muted-foreground" />
) : (
<Users className="h-4 w-4 text-muted-foreground" />
)}
</div>

<div className="flex-1 text-left min-w-0">
<div className="flex items-center justify-between">
<span className="font-medium text-sm truncate">{conversation.roomName}</span>
{conversation.memberCount !== undefined && conversation.memberCount > 0 && (
<Badge variant="secondary" className="text-xs h-5 flex-shrink-0">
{conversation.memberCount}
</Badge>
)}
</div>
{conversation.lastMessage && (
<div className="space-y-0.5">
<p className="text-xs text-muted-foreground truncate">
{conversation.lastMessage.user.firstName || conversation.lastMessage.user.lastName
? `${conversation.lastMessage.user.firstName || ''} ${conversation.lastMessage.user.lastName || ''}`.trim()
: 'Usuário'
}: {conversation.lastMessage.content || 'Mensagem sem texto'}
</p>
<p className="text-xs text-muted-foreground/70">
{(() => {
const now = new Date()
const messageTime = new Date(conversation.lastMessage.createdAt)
const diffMs = now.getTime() - messageTime.getTime()
const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
const diffDays = Math.floor(diffHours / 24)

<div className="flex-1 text-left">
<div className="flex items-center justify-between">
<span className="font-medium text-sm">{room.name}</span>
{room.memberCount !== undefined && room.memberCount > 0 && (
<Badge variant="secondary" className="text-xs h-5">
{room.memberCount}
</Badge>
if (diffHours < 1) {
const diffMinutes = Math.floor(diffMs / (1000 * 60))
return diffMinutes <= 1 ? 'agora' : `${diffMinutes}min atrás`
} else if (diffHours < 24) {
return `${diffHours}h atrás`
} else if (diffDays === 1) {
return 'ontem'
} else {
return `${diffDays}d atrás`
}
})()}
</p>
</div>
)}
</div>
<p className="text-xs text-muted-foreground truncate">
{room.description}
</p>
</div>
</div>
</Button>
))}
</Button>
))}
</div>
</div>
)}

{/* Todas as Salas */}
<div className="p-2">
<div className="px-2 py-1">
<h3 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
Todas as Salas
</h3>
</div>

{isLoading ? (
<div className="space-y-2">
{[1, 2, 3].map((i) => (
<div key={i} className="h-16 bg-muted rounded animate-pulse"></div>
))}
</div>
) : (
<div className="space-y-1">
{allRooms.map((room) => (
<Button
key={room.id}
variant={currentRoomId === room.id ? "secondary" : "ghost"}
className={cn(
"w-full justify-start h-auto p-3",
currentRoomId === room.id && "bg-primary/10"
)}
onClick={() => onRoomChange(room.id)}
>
<div className="flex items-center gap-3 w-full">
<div className="flex-shrink-0">
{room.type === 'global' ? (
<Hash className="h-4 w-4 text-muted-foreground" />
) : (
<Users className="h-4 w-4 text-muted-foreground" />
)}
</div>

<div className="flex-1 text-left">
<div className="flex items-center justify-between">
<span className="font-medium text-sm">{room.name}</span>
{room.memberCount !== undefined && room.memberCount > 0 && (
<Badge variant="secondary" className="text-xs h-5">
{room.memberCount}
</Badge>
)}
</div>
<p className="text-xs text-muted-foreground truncate">
{room.description}
</p>
</div>
</div>
</Button>
))}
</div>
)}
</div>
</div>
</div>
)
Expand Down
Loading
Loading