FEAT: Ajustado espaço delimitador da tela de chat e conversas ativas

This commit is contained in:
Rafael Alves Lopes 2026-05-19 09:45:00 -03:00
parent bff4e18094
commit 7dc07c2a80
4 changed files with 86 additions and 10 deletions

View File

@ -23,6 +23,29 @@ function ChannelBadge({ channel }) {
); );
} }
function PresenceDot({ status }) {
const color =
status === 'online'
? '#16a34a'
: status === 'away'
? '#e5a22a'
: '#dc2626';
return (
<span
aria-hidden="true"
style={{
width: 10,
height: 10,
borderRadius: 999,
background: color,
boxShadow: `0 0 0 3px ${color}22`,
flex: '0 0 auto',
}}
/>
);
}
export function ChatConversationList({ export function ChatConversationList({
contacts, contacts,
activeContactId, activeContactId,
@ -37,7 +60,10 @@ export function ChatConversationList({
borderRadius: '28px', borderRadius: '28px',
padding: '1rem', padding: '1rem',
display: 'grid', display: 'grid',
gridTemplateRows: 'auto minmax(0, 1fr)',
gap: '0.85rem', gap: '0.85rem',
height: isMobile ? 'auto' : 'min(760px, calc(100vh - 190px))',
minHeight: 0,
}} }}
> >
<div> <div>
@ -52,6 +78,9 @@ export function ChatConversationList({
display: 'grid', display: 'grid',
gap: '0.75rem', gap: '0.75rem',
gridTemplateColumns: isMobile ? '1fr' : '1fr', gridTemplateColumns: isMobile ? '1fr' : '1fr',
overflowY: 'auto',
minHeight: 0,
paddingRight: '0.15rem',
}} }}
> >
{contacts.map((contact) => { {contacts.map((contact) => {
@ -74,7 +103,12 @@ export function ChatConversationList({
}} }}
> >
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}> <div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}>
<strong>{contact.name}</strong> <span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', minWidth: 0 }}>
<PresenceDot status={contact.status} />
<strong style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{contact.name}
</strong>
</span>
<span style={{ fontSize: '0.82rem', color: 'var(--color-text-soft)' }}> <span style={{ fontSize: '0.82rem', color: 'var(--color-text-soft)' }}>
{contact.time} {contact.time}
</span> </span>

View File

@ -179,6 +179,40 @@ function AttachmentPreview({ file, onRemove }) {
); );
} }
function ContactPresence({ contact }) {
const status = contact.status || 'offline';
const color =
status === 'online'
? '#16a34a'
: status === 'away'
? '#e5a22a'
: '#dc2626';
const label = status === 'online' ? 'Online agora' : contact.lastSeen || 'Offline';
return (
<span
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
color: 'var(--color-text-soft)',
}}
>
<span
aria-hidden="true"
style={{
width: 10,
height: 10,
borderRadius: 999,
background: color,
boxShadow: `0 0 0 3px ${color}22`,
}}
/>
{label}
</span>
);
}
export function ChatWindow({ export function ChatWindow({
contact, contact,
messages, messages,
@ -217,8 +251,10 @@ export function ChatWindow({
borderRadius: '28px', borderRadius: '28px',
overflow: 'hidden', overflow: 'hidden',
display: 'grid', display: 'grid',
gridTemplateRows: 'auto 1fr auto', gridTemplateRows: 'auto minmax(0, 1fr) auto',
minHeight: 680, height: isMobile ? 'auto' : 'min(760px, calc(100vh - 190px))',
minHeight: isMobile ? 640 : 0,
minWidth: 0,
}} }}
> >
<header <header
@ -233,9 +269,7 @@ export function ChatWindow({
> >
<div> <div>
<strong style={{ display: 'block', fontSize: '1.15rem' }}>{contact.name}</strong> <strong style={{ display: 'block', fontSize: '1.15rem' }}>{contact.name}</strong>
<span style={{ color: 'var(--color-text-soft)' }}> <ContactPresence contact={contact} />
{contact.status === 'online' ? 'Online' : 'Offline'} {contact.lastSeen}
</span>
</div> </div>
<div <div
@ -286,6 +320,7 @@ export function ChatWindow({
gap: '0.9rem', gap: '0.9rem',
alignContent: 'start', alignContent: 'start',
overflowY: 'auto', overflowY: 'auto',
minHeight: 0,
background: background:
'radial-gradient(circle at top left, rgba(0, 164, 183, 0.06), transparent 22%), linear-gradient(180deg, rgba(245, 248, 251, 0.8), rgba(255, 255, 255, 0.95))', 'radial-gradient(circle at top left, rgba(0, 164, 183, 0.06), transparent 22%), linear-gradient(180deg, rgba(245, 248, 251, 0.8), rgba(255, 255, 255, 0.95))',
}} }}

View File

@ -40,13 +40,20 @@ function getPreviewFromMessage(message) {
function normalizeChat(chat) { function normalizeChat(chat) {
const id = getSerializedId(chat.id); const id = getSerializedId(chat.id);
const lastActivitySeconds = chat.timestamp ? Math.floor(Date.now() / 1000) - chat.timestamp : null;
const isRecentlyActive = lastActivitySeconds !== null && lastActivitySeconds < 300;
return { return {
id, id,
name: getContactName(chat), name: getContactName(chat),
channel: 'WhatsApp', channel: 'WhatsApp',
status: 'online', status: isRecentlyActive ? 'online' : 'away',
area: chat.assignment?.area_id ? String(chat.assignment.area_id) : 'Suporte', area: chat.assignment?.area_id ? String(chat.assignment.area_id) : 'Suporte',
lastSeen: chat.timestamp ? `Visto as ${formatTime(chat.timestamp)}` : 'Online agora', lastSeen: isRecentlyActive
? 'Online agora'
: chat.timestamp
? `Visto as ${formatTime(chat.timestamp)}`
: 'Sem atividade recente',
preview: chat.preview || chat.lastMessage?.body || '', preview: chat.preview || chat.lastMessage?.body || '',
time: formatTime(chat.timestamp) || 'Agora', time: formatTime(chat.timestamp) || 'Agora',
unread: chat.unreadCount || 0, unread: chat.unreadCount || 0,

View File

@ -103,7 +103,7 @@ export function ChatPage() {
display: 'grid', display: 'grid',
gridTemplateColumns, gridTemplateColumns,
gap: '1rem', gap: '1rem',
alignItems: 'start', alignItems: 'stretch',
}} }}
> >
<ChatConversationList <ChatConversationList
@ -113,7 +113,7 @@ export function ChatPage() {
isMobile={isMobile} isMobile={isMobile}
/> />
<div style={{ display: 'grid', gap: '1rem', minWidth: 0 }}> <div style={{ display: 'grid', gap: '1rem', minWidth: 0, alignContent: 'start' }}>
<ChatWindow <ChatWindow
contact={activeContact} contact={activeContact}
messages={messages} messages={messages}