InternalChat

src/layouts/components/internal-chat-message/ — 4 sub-componentes: InternalChatContact, InternalChatMessage, InternalChatLog, InternalChatLogItem
Communication High 3 usos

Sistema completo de chat interno entre agentes da plataforma. Composto por 4 componentes aninhados. Suporta texto, imagens, áudio (gravação via MediaRecorder), vídeo, documentos, replies e mensagens fixadas. Totalmente integrado com Vuex internalChatMessages.

Lista de Contatos

InternalChatContact — painel fixo no canto inferior direito da tela (position: fixed). Exibe lista de agentes internos com indicador de mensagens não lidas. Linhas com unseenMsg > 0 recebem fundo amarelo/warning.

3
Chat Interno
Buscar agente...
JS
2
João Silva
Você viu o chamado #448?
2
MS
Maria Souza
Ok, já verifico!
PC
Pedro Costa
Pedido foi confirmado
position: fixed right: 8.57rem bottom: 0 width: 24.9rem height: 33rem
Real src/components/button/ButtonFloating.vue:28
<template v-if="!hasDpsp">
  <InternalChatContact
    :isActive="interChatIsActive"
    @setInternalChatValue="setInternalChatValue()"
  />

  <!-- Barra fixa 'Chat Interno' — aparece quando painel fechado e usuário está logado -->
  <div
    v-if="validate && !twoFactorActivated && !interChatIsActive && !isChatAttendancePage"
    class="chat-internal bg-primary select-none cursor-pointer"
    @click="openInternalChat"
  >
    <div class="flex w-full">
      <vs-avatar :badge="chatNotification" class="icon-navbar bg-primary"/>
      <span class="text-white align-self-center">Chat Interno</span>
      <feather-icon class="text-white" icon="ChevronUpIcon"/>
    </div>
  </div>
</template>

Conversa

InternalChatMessage — tela de conversa com header azul, área de mensagens (InternalChatLog) e barra de input. O header expõe ChevronLeft para voltar à lista e ChevronDown para fechar o sistema inteiro.

MS
Maria Souza
online
Oi Maria! Você pode verificar o pedido #1234?
10:42
MS
Claro, já estou verificando o status dele.
10:43
Obrigado! Cliente está aguardando retorno.
10:44
Digite uma mensagem...
Real src/layouts/components/internal-chat-message/InternalChatContact.vue:3
<!-- InternalChatMessage nunca é usado diretamente em views externas. -->
<InternalChatMessage
  :selectedUser="selectedUser"
  :upInternalChat="openInternalChat"
  @backInternalChat="closeInternalChat()"
  @closeListChat="closeListChat"
/>

Bolhas de Mensagem

InternalChatLogItem — alinhamento controlado por chat.type e pela prop type. Agente renderiza à direita com gradiente azul; cliente à esquerda com fundo branco e borda. Replies são aninhados via slot padrão.

Agente (direita) — chat.type === 0
Pedido foi confirmado!
09:15
Cliente (esquerda) — chat.type === 1
CL
Preciso verificar o status
09:12
Com reply — slot padrão aninhado
Maria Souza
Preciso verificar o status do pedid...
Sim! Pedido #1234 foi confirmado agora.
09:16
Sintético
<!-- Bolha do agente (direita) -->
<InternalChatLogItem
  :chat="chatMsg"
  :index="idx"
  type="agent-agent"
/>

<!-- Bolha do cliente (esquerda) -->
<InternalChatLogItem
  :chat="chatMsg"
  :index="idx"
  type="customer-agent"
/>

<!-- Com reply — mensagem citada no slot padrão -->
<InternalChatLogItem
  :chat="replyMsg"
  :index="idx"
  :principalIndex="parentMsgId"
>
  <InternalChatLogItem
    :chat="quotedMsg"
    :index="quotedIdx"
    :hiddenReply="true"
  />
</InternalChatLogItem>

Mensagem Fixada

Barra sticky no topo do InternalChatLog. Suporta navegação entre múltiplas mensagens fixadas via ChevronRight. Click navega até a mensagem com scrollIntoView + highlight de 2s. Texto truncado em 25 caracteres. Botão X desafixa.

Mensagem fixada
Pedido #1234 foi conf...
Pedido #1234 foi confirmado!
08:30
CL
Perfeito, obrigado!
08:32

Mensagem com highlight de 2s após navegação via barra fixada.

Sintético
<!-- Dentro de InternalChatLog -->
<div
  v-if="pinnedMessages.length"
  class="pinned-messages-bar"
  @click="scrollToPinned"
>
  <feather-icon icon="MapPinIcon" class="text-warning" />
  <span class="pinned-text">{{ currentPinned.message | truncate(25) }}</span>
  <feather-icon
    icon="ChevronRightIcon"
    class="text-primary"
    @click.stop="nextPinned"
  />
  <feather-icon
    icon="XIcon"
    class="text-danger"
    @click.stop="unpinMessage(currentPinned.id)"
  />
</div>

Gravação de Áudio

Estado de gravação via MediaRecorder API. A barra de input troca para modo de gravação: XCircleIcon cancela, timer vermelho piscando + contador, CheckCircleIcon envia.

Vou enviar um áudio explicando
14:20
00:47

Vermelho = cancelar gravação  |  Verde = enviar áudio

Sintético
<!-- Barra de input em modo gravação -->
<div v-if="isRecording" class="audio-recording-bar">
  <feather-icon
    icon="XCircleIcon"
    class="text-danger cursor-pointer"
    @click="cancelRecording"
  />
  <span class="recording-dot"></span>
  <span class="recording-timer">{{ recordingTime }}</span>
  <feather-icon
    icon="CheckCircleIcon"
    class="text-success cursor-pointer"
    @click="sendAudio"
  />
</div>

<!-- Barra normal -->
<div v-else class="input-bar">
  <feather-icon icon="PaperclipIcon" @click="openAttachment" />
  <vs-input v-model="message" placeholder="Digite uma mensagem..." />
  <feather-icon icon="MicIcon" @click="startRecording" />
  <vs-button @click="sendMessage">
    <feather-icon icon="SendIcon" />
  </vs-button>
</div>

API

Arquitetura

InternalChatContact
InternalChatMessage
InternalChatLog
InternalChatLogItem
slot: reply (LogItem aninhado)

Sub-componentes

ComponentePapelRenderização
InternalChatContactEntry point. Lista de agentes internos. Painel fixo bottom-right.Sempre via v-if="isActive"
InternalChatMessageTela de conversa. Header + InternalChatLog + barra de input.Dentro de InternalChatContact
InternalChatLogÁrea de mensagens scrollável. Barra de pinned. VuePerfectScrollbar.Dentro de InternalChatMessage
InternalChatLogItemBolha individual. Suporta slot para reply aninhado.Loop dentro de InternalChatLog

Props — InternalChatContact

NomeTipoDefaultRequiredDescrição
isActiveBooleanfalsefalseControla visibilidade do painel (v-if)

Eventos — InternalChatContact

EventoPayloadQuando
setInternalChatValueBooleanEmitido para fechar o painel. Pai deve setar isActive = false

Props — InternalChatMessage

NomeTipoDefaultRequiredDescrição
upInternalChatBooleantrueControla visibilidade (v-if)
selectedUserObject|nulltrueUsuário da conversa: { id, username, image? }

Eventos — InternalChatMessage

EventoPayloadQuando
backInternalChatnullClick em ChevronLeft — volta para lista de contatos
closeListChatnullClick em ChevronDown — fecha todo o sistema

Props — InternalChatLog

NomeTipoDefaultRequiredDescrição
upInternalChatBooleantrueControla carregamento inicial das mensagens
activeChatUserObject|nulltrueUsuário da conversa: { id }

Props — InternalChatLogItem

NomeTipoDefaultRequiredDescrição
chatObject|nullfalseMensagem: { id, message, type, tag_type, created_at, ack, is_pinned, is_temp, document_name }
indexNumbertrueIndex na lista de mensagens
typeStringnullfalse'agent-agent', 'customer-customer', 'agent-customer', 'customer-agent', 'customer'
hiddenReplyBooleanfalsefalseQuando true: oculta botões reply e pin (usado no slot de reply)
principalIndexNumbernullfalseID da mensagem pai no reply

Slot — InternalChatLogItem

SlotEscopadoDescrição
defaultNãoReply aninhado: a mensagem atual envolve o slot que contém a mensagem citada (InternalChatLogItem com :hiddenReply="true")

Lógica de alinhamento

DireçãoCondiçãoClasse CSS
direita chat.type === 0 (agente) OU type === 'customer-agent' flex-row-reverse
esquerda chat.type === 1 (cliente) border cinza + bg-white
força esquerda type === 'customer-customer' OU 'agent-customer' force-revert-flex

Estilos de bolha

TipoClasse / Estilo
Agente (normal)bg-primary-gradient text-white
Agente (bot)is-bot-message text-white
Comentáriois-comment-message text-white
Clienteborder border-solid border-grey-light bg-white

Renderização de mídia

TipoRenderização
image / sticker<img :src="chat.message"> — max-width 100%
audio<audio controls><source :src></audio>
video<video controls style="width:240px"><source type="video/mp4"></video>
documentdocument_name + DownloadIcon + "Baixar documento". Download via axios blob.
textv-html com stylizeMessage(). white-space: pre-line.

Ícones de ACK (Material Icons)

ValorÍconeSignificado
0scheduleEnviando...
1doneEntregue
2done_all (cinza)Lido (cinza)
3done_all (azul)Lido (azul)
-1report_problemErro no envio

Mensagens fixadas

FuncionalidadeDetalhe
Quantidade por vez1 exibida por vez; ChevronRight navega entre múltiplas
Truncamento25 caracteres
DesafixarBotão X vermelho chama unpinMessage(id)
NavegaçãoClick faz scrollIntoView + highlight da bolha por 2s

Event Bus ($events)

EventoEmitido porDescrição
OpenInternalChatInternalChatContactAbre o chat interno programaticamente de fora do componente
closeInternalChatMessageComponentsInternalChatMessageFecha popup de imagem

Dependências

TipoDependência
Vuesaxvs-input, vs-avatar, vs-popup (3×), vs-button, vs-dropdown, vs-divider, vs-chip, $vs.loading
CustomUpload (src/components/upload/Upload.vue), VuePerfectScrollbar
Feather IconsChevronDownIcon, ChevronLeftIcon, PaperclipIcon, XCircleIcon, CheckCircleIcon, DownloadIcon, PinIcon, ChevronRightIcon, XIcon
Externalmoment (formatação de datas), axios (download de documentos)
Vuex StoreinternalChatMessages — fetch, send, pin, reply, reset
Vuex Storeuser/fetchInternalPaginated
Event Bus$events

Onde é usado

ArquivoNota
src/layouts/main/Main.vueLayout principal — monta InternalChatContact globalmente
src/components/button/ButtonFloating.vueBotão flutuante com barra de acesso rápido
src/views/chat-attendance/Index.vueTela de atendimento — integração com chat de agentes