FEAT: Adição de horas as mensgens

This commit is contained in:
Rafael Alves Lopes 2026-05-19 16:39:01 -03:00
parent 217d566057
commit 2e97ac6e5a
6 changed files with 53 additions and 43 deletions

View File

@ -23,13 +23,8 @@ function ChannelBadge({ channel }) {
);
}
function PresenceDot({ status }) {
const color =
status === 'online'
? '#16a34a'
: status === 'away'
? '#e5a22a'
: '#dc2626';
function ActivityDot({ status }) {
const color = status === 'away' ? '#e5a22a' : '#dc2626';
return (
<span
@ -134,7 +129,7 @@ export function ChatConversationList({
>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', minWidth: 0 }}>
<PresenceDot status={contact.status} />
<ActivityDot status={contact.status} />
<strong style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{contact.name}
</strong>

View File

@ -22,6 +22,13 @@ function parseMessageText(text) {
};
}
function formatMessageTime(timestamp) {
if (!timestamp) return '';
const numericTimestamp = Number(timestamp);
const date = new Date(numericTimestamp > 1000000000000 ? numericTimestamp : numericTimestamp * 1000);
return date.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
}
function MediaRenderer({ message, contactId, onLoadMedia, isAgent }) {
const mediaUrl = useMemo(() => getMediaUrl(message.media), [message.media]);
const mimetype = message.media?.mimetype || '';
@ -196,19 +203,14 @@ function AttachmentPreview({ file, onRemove }) {
);
}
function ContactPresence({ contact }) {
function ContactActivity({ contact }) {
if (!contact) {
return null;
}
const status = contact.status || 'offline';
const color =
status === 'online'
? '#16a34a'
: status === 'away'
? '#e5a22a'
: '#dc2626';
const label = status === 'online' ? 'Online agora' : contact.lastSeen || 'Offline';
const color = status === 'away' ? '#e5a22a' : '#dc2626';
const label = contact.lastSeen || 'Sem atividade recente';
return (
<span
@ -301,7 +303,7 @@ export function ChatWindow({
>
<div>
<strong style={{ display: 'block', fontSize: '1.15rem' }}>{safeContact.name}</strong>
<ContactPresence contact={safeContact} />
<ContactActivity contact={safeContact} />
</div>
<div
@ -397,6 +399,7 @@ export function ChatWindow({
const isAgent = message.sender === 'agent';
const isSystem = message.sender === 'system';
const parsedText = parseMessageText(message.text);
const messageTime = formatMessageTime(message.timestamp);
if (isSystem) {
return (
@ -464,6 +467,18 @@ export function ChatWindow({
{parsedText.body}
</span>
) : null}
{messageTime ? (
<span
style={{
justifySelf: 'end',
fontSize: '0.72rem',
lineHeight: 1,
color: isAgent ? 'rgba(255,255,255,0.7)' : 'var(--color-text-soft)',
}}
>
{messageTime}
</span>
) : null}
</div>
);
})}

View File

@ -38,22 +38,17 @@ function getPreviewFromMessage(message) {
function normalizeChat(chat) {
const id = getSerializedId(chat.id);
const lastActivitySeconds = chat.timestamp ? Math.floor(Date.now() / 1000) - chat.timestamp : null;
const isRecentlyActive = lastActivitySeconds !== null && lastActivitySeconds < 300;
const assignment = chat.assignment || null;
const lastSeenTimestamp = chat.timestamp || null;
return {
id,
name: getContactName(chat),
channel: 'WhatsApp',
status: isRecentlyActive ? 'online' : 'away',
status: lastSeenTimestamp ? 'away' : 'offline',
area: assignment?.area_nome || (assignment?.area_id ? String(assignment.area_id) : 'Sem fila'),
areaId: assignment?.area_id || null,
lastSeen: isRecentlyActive
? 'Online agora'
: chat.timestamp
? `Visto as ${formatTime(chat.timestamp)}`
: 'Sem atividade recente',
lastSeen: lastSeenTimestamp ? `Ultima atividade as ${formatTime(lastSeenTimestamp)}` : 'Sem atividade recente',
preview: chat.preview || chat.lastMessage?.body || '',
time: formatTime(chat.timestamp) || 'Agora',
unread: chat.unreadCount || 0,
@ -77,6 +72,11 @@ function normalizeMessage(message) {
};
}
function isDisplayableMessage(message) {
const text = String(message?.text ?? message?.body ?? '').trim();
return Boolean(text || message?.hasMedia || message?.media);
}
function getComparableMessageTime(message) {
if (message.timestamp) return Number(message.timestamp);
if (typeof message.id === 'string' && message.id.startsWith('temp-')) {
@ -316,10 +316,12 @@ export function useChat() {
setMessagesByContact((current) => ({
...current,
[activeContactId]: dedupeMessages(
data.map((message) => ({
...normalizeMessage(message),
chatId: activeContactId,
})),
data
.map((message) => ({
...normalizeMessage(message),
chatId: activeContactId,
}))
.filter(isDisplayableMessage),
),
}));
setApiError(null);
@ -345,6 +347,10 @@ export function useChat() {
...normalizeMessage(incomingMessage),
chatId: contactId,
};
if (!isDisplayableMessage(message)) {
clearIncomingMessage();
return;
}
const preview = getPreviewFromMessage(message);
setMessagesByContact((current) => {
@ -367,14 +373,16 @@ export function useChat() {
id: contactId,
name: incomingMessage.notifyName || contactId.split('@')[0],
channel: 'WhatsApp',
status: 'online',
status: 'away',
area: 'Sem fila',
lastSeen: 'Online agora',
lastSeen: 'Visto agora',
unread: 0,
assignment: null,
}),
preview,
time: 'Agora',
status: 'away',
lastSeen: 'Ultima atividade agora',
unread:
incomingMessage.fromMe || contactId === activeContactRef.current
? 0

View File

@ -3,9 +3,9 @@ export const chatContacts = [
id: 'maria-souza',
name: 'Maria Souza',
channel: 'WhatsApp',
status: 'online',
status: 'away',
area: 'Suporte',
lastSeen: 'Online agora',
lastSeen: 'Ultima atividade as 09:42',
preview: 'Preciso atualizar o cadastro do meu pedido.',
time: '09:42',
unread: 2,
@ -22,7 +22,7 @@ export const chatContacts = [
channel: 'SMS',
status: 'offline',
area: 'Financeiro',
lastSeen: 'Visto ha 12 min',
lastSeen: 'Ultima atividade as 08:15',
preview: 'Pode me ligar em 10 minutos?',
time: '08:15',
unread: 1,

View File

@ -301,7 +301,7 @@ export function MessagesWorkspace({
{safeActiveConversation.name}
</strong>
<span style={{ color: 'var(--color-text-soft)' }}>
{safeActiveConversation.status === 'online' ? 'Online agora' : 'Offline'}
{safeActiveConversation.lastSeen || 'Sem atividade recente'}
</span>
</div>
<div style={{ display: 'flex', gap: '0.6rem', flexWrap: 'wrap' }}>

View File

@ -7,7 +7,6 @@ export function useWhatsappSocket() {
const [qrCode, setQrCode] = useState(null);
const [status, setStatus] = useState('DISCONNECTED');
const [incomingMessage, setIncomingMessage] = useState(null);
const [presenceUpdate, setPresenceUpdate] = useState(null);
const socketRef = useRef(null);
useEffect(() => {
@ -44,11 +43,6 @@ export function useWhatsappSocket() {
setIncomingMessage(message);
});
newSocket.on('presence', (presence) => {
console.log('Atualização de presença:', presence);
setPresenceUpdate(presence);
});
return () => {
newSocket.disconnect();
socketRef.current = null;
@ -60,8 +54,6 @@ export function useWhatsappSocket() {
qrCode,
status,
incomingMessage,
presenceUpdate,
clearIncomingMessage: () => setIncomingMessage(null),
clearPresenceUpdate: () => setPresenceUpdate(null)
};
}