FEAT: Editado Home page para se adequar ao serviço de pausa
This commit is contained in:
parent
fe40e8bd76
commit
4b0a4bb3e3
@ -318,14 +318,16 @@ export function ChatWindow({
|
||||
assignmentLabel,
|
||||
transferNote,
|
||||
isReplying,
|
||||
isPaused = false,
|
||||
pauseDurationLabel = '00:00',
|
||||
isMobile = false,
|
||||
}) {
|
||||
const messagesRef = useRef(null);
|
||||
const safeContact = contact || {
|
||||
id: '',
|
||||
name: 'Nenhuma conversa ativa',
|
||||
name: isPaused ? 'Atendimento pausado' : 'Nenhuma conversa ativa',
|
||||
status: 'offline',
|
||||
lastSeen: 'Aguardando fila do Omnino',
|
||||
lastSeen: isPaused ? `Pausa em andamento: ${pauseDurationLabel}` : 'Aguardando fila do Omnino',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -340,6 +342,62 @@ export function ChatWindow({
|
||||
});
|
||||
}, [messages, isReplying]);
|
||||
|
||||
if (isPaused) {
|
||||
return (
|
||||
<section
|
||||
style={{
|
||||
background: '#fff',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '28px',
|
||||
overflow: 'hidden',
|
||||
display: 'grid',
|
||||
gridTemplateRows: 'auto minmax(0, 1fr)',
|
||||
height: isMobile ? 'auto' : 'min(760px, calc(100vh - 190px))',
|
||||
minHeight: isMobile ? 420 : 0,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<header
|
||||
style={{
|
||||
padding: '1.25rem 1.5rem',
|
||||
borderBottom: '1px solid var(--color-border)',
|
||||
}}
|
||||
>
|
||||
<strong style={{ display: 'block', fontSize: '1.15rem' }}>Atendimento pausado</strong>
|
||||
<span style={{ color: 'var(--color-text-soft)' }}>Pausa em andamento: {pauseDurationLabel}</span>
|
||||
</header>
|
||||
|
||||
<div
|
||||
style={{
|
||||
padding: '1.5rem',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
minHeight: 0,
|
||||
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))',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 460,
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: 20,
|
||||
padding: '1.2rem',
|
||||
background: '#fff',
|
||||
color: 'var(--color-text-soft)',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.5,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Voce esta em pausa ha {pauseDurationLabel}. Retome o atendimento pela Home para visualizar a fila,
|
||||
assumir chamados e responder clientes.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
style={{
|
||||
@ -572,7 +630,7 @@ export function ChatWindow({
|
||||
);
|
||||
})}
|
||||
|
||||
{messages.length === 0 ? (
|
||||
{messages.length === 0 ? (
|
||||
<div
|
||||
style={{
|
||||
justifySelf: 'center',
|
||||
@ -583,7 +641,9 @@ export function ChatWindow({
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
Nenhuma mensagem carregada.
|
||||
{isPaused
|
||||
? `Voce esta em pausa ha ${pauseDurationLabel}. Volte da pausa para visualizar a fila e seus atendimentos.`
|
||||
: 'Nenhuma mensagem carregada.'}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@ -625,7 +685,9 @@ export function ChatWindow({
|
||||
}}
|
||||
>
|
||||
<span style={{ display: 'block' }}>
|
||||
{canAssumeChat
|
||||
{isPaused
|
||||
? `Voce esta em pausa ha ${pauseDurationLabel}. Nenhum atendimento sera exibido ate voce voltar.`
|
||||
: canAssumeChat
|
||||
? 'Este atendimento está na fila. Assuma para responder ou transferir.'
|
||||
: assignmentLabel || 'Este atendimento está atribuído a outro usuário.'}
|
||||
</span>
|
||||
@ -681,7 +743,9 @@ export function ChatWindow({
|
||||
}}
|
||||
disabled={!safeContact.id || !canReply}
|
||||
placeholder={
|
||||
!safeContact.id
|
||||
isPaused
|
||||
? 'Voce esta em pausa'
|
||||
: !safeContact.id
|
||||
? 'Aguardando conversa entrar em uma fila'
|
||||
: canReply
|
||||
? 'Escreva sua mensagem...'
|
||||
|
||||
@ -4,9 +4,33 @@ import { API_BASE_URL } from '../../../shared/services/apiConfig';
|
||||
import { getAccessOptions, getAccessUsers } from '../../management/services/adminAccessService';
|
||||
import { getCurrentUser, getCurrentUserProfile } from '../../auth/services/sessionService';
|
||||
import { transferAreas as fallbackTransferAreas } from '../services/chatMocks';
|
||||
import {
|
||||
getAgentPresence,
|
||||
listAgentPresence,
|
||||
pauseAgent,
|
||||
resumeAgent,
|
||||
} from '../services/agentPresenceService';
|
||||
|
||||
const MAX_ATTACHMENT_SIZE_BYTES = 15 * 1024 * 1024;
|
||||
|
||||
function getPresenceByUserId(presenceList, userId) {
|
||||
return presenceList.find((presence) => Number(presence.user_id) === Number(userId)) || null;
|
||||
}
|
||||
|
||||
function formatPauseDuration(totalSeconds) {
|
||||
const seconds = Math.max(0, Number(totalSeconds || 0));
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = seconds % 60;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const remainingMinutes = minutes % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${String(remainingMinutes).padStart(2, '0')}m`;
|
||||
}
|
||||
|
||||
return `${String(remainingMinutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function getLastMessageFromMe(messages = []) {
|
||||
const lastMessage = [...messages].reverse().find(isDisplayableMessage);
|
||||
if (!lastMessage) return false;
|
||||
@ -210,6 +234,10 @@ export function useChat() {
|
||||
const [attachedFile, setAttachedFile] = useState(null);
|
||||
const [areaOptions, setAreaOptions] = useState([]);
|
||||
const [accessUsers, setAccessUsers] = useState([]);
|
||||
const [presenceList, setPresenceList] = useState([]);
|
||||
const [agentPresence, setAgentPresence] = useState(null);
|
||||
const [pauseSeconds, setPauseSeconds] = useState(0);
|
||||
const [isPresenceLoading, setIsPresenceLoading] = useState(false);
|
||||
const [selectedArea, setSelectedArea] = useState('Sem fila');
|
||||
const [isTransferOpen, setIsTransferOpen] = useState(false);
|
||||
const [transferArea, setTransferArea] = useState(currentUserAreas[0] || 'Suporte');
|
||||
@ -221,6 +249,7 @@ export function useChat() {
|
||||
const [apiError, setApiError] = useState(null);
|
||||
const activeContactRef = useRef(activeContactId);
|
||||
const contactsRef = useRef(contacts);
|
||||
const isPaused = agentPresence?.status === 'paused';
|
||||
|
||||
const activeContact = useMemo(
|
||||
() => {
|
||||
@ -240,8 +269,12 @@ export function useChat() {
|
||||
const usersInTransferArea = accessUsers.filter((user) =>
|
||||
user.areas?.some((area) => area.nome === transferArea) || user.areaPrincipal?.nome === transferArea,
|
||||
);
|
||||
const availableUsersInTransferArea = usersInTransferArea.filter((user) => {
|
||||
const presence = getPresenceByUserId(presenceList, user.id);
|
||||
return !presence || presence.status === 'available';
|
||||
});
|
||||
const isSameUserArea = currentUserAreas.includes(transferArea);
|
||||
const attendants = isSameUserArea ? usersInTransferArea : [];
|
||||
const attendants = isSameUserArea ? availableUsersInTransferArea : [];
|
||||
const activeAssignment = activeContact?.assignment || null;
|
||||
const isAssignedToCurrentUser = Boolean(
|
||||
activeAssignment?.user_id && currentUserId && Number(activeAssignment.user_id) === currentUserId,
|
||||
@ -251,8 +284,8 @@ export function useChat() {
|
||||
activeAssignment?.status === 'queued' &&
|
||||
(isAdminUser || !activeAssignment.area_nome || currentUserAreas.includes(activeAssignment.area_nome)),
|
||||
);
|
||||
const canAssumeChat = Boolean(activeContact?.id?.includes('@') && currentUserId && isQueuedForUserArea);
|
||||
const canReply = Boolean(isAssignedToCurrentUser && !isWaitingCustomerReply);
|
||||
const canAssumeChat = Boolean(!isPaused && activeContact?.id?.includes('@') && currentUserId && isQueuedForUserArea);
|
||||
const canReply = Boolean(!isPaused && isAssignedToCurrentUser && !isWaitingCustomerReply);
|
||||
const assignmentLabel = activeAssignment?.user_id
|
||||
? isWaitingCustomerReply
|
||||
? 'Aguardando resposta do cliente para liberar novas mensagens'
|
||||
@ -268,7 +301,7 @@ export function useChat() {
|
||||
|
||||
useEffect(() => {
|
||||
setTransferAttendant(attendants[0]?.id ? String(attendants[0].id) : '');
|
||||
}, [transferArea, accessUsers]);
|
||||
}, [transferArea, accessUsers, presenceList]);
|
||||
|
||||
useEffect(() => {
|
||||
activeContactRef.current = activeContactId;
|
||||
@ -283,14 +316,23 @@ export function useChat() {
|
||||
|
||||
async function loadAccessData() {
|
||||
try {
|
||||
const [options, users] = await Promise.all([getAccessOptions(), getAccessUsers()]);
|
||||
const [options, users, presences, currentPresence] = await Promise.all([
|
||||
getAccessOptions(),
|
||||
getAccessUsers(),
|
||||
listAgentPresence(),
|
||||
currentUserId ? getAgentPresence(currentUserId) : Promise.resolve(null),
|
||||
]);
|
||||
if (!isMounted) return;
|
||||
setAreaOptions(options.areas || []);
|
||||
setAccessUsers(users || []);
|
||||
setPresenceList(Array.isArray(presences) ? presences : []);
|
||||
setAgentPresence(currentPresence);
|
||||
setPauseSeconds(Number(currentPresence?.paused_seconds || 0));
|
||||
} catch {
|
||||
if (isMounted) {
|
||||
setAreaOptions([]);
|
||||
setAccessUsers([]);
|
||||
setPresenceList([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,7 +341,43 @@ export function useChat() {
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
}, [currentUserId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUserId) return undefined;
|
||||
|
||||
let isMounted = true;
|
||||
async function refreshPresence() {
|
||||
try {
|
||||
const [presences, currentPresence] = await Promise.all([
|
||||
listAgentPresence(),
|
||||
getAgentPresence(currentUserId),
|
||||
]);
|
||||
if (!isMounted) return;
|
||||
setPresenceList(Array.isArray(presences) ? presences : []);
|
||||
setAgentPresence(currentPresence);
|
||||
setPauseSeconds(Number(currentPresence?.paused_seconds || 0));
|
||||
} catch {
|
||||
if (isMounted) setPresenceList([]);
|
||||
}
|
||||
}
|
||||
|
||||
const intervalId = window.setInterval(refreshPresence, 30000);
|
||||
return () => {
|
||||
isMounted = false;
|
||||
window.clearInterval(intervalId);
|
||||
};
|
||||
}, [currentUserId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPaused) return undefined;
|
||||
|
||||
const intervalId = window.setInterval(() => {
|
||||
setPauseSeconds((current) => current + 1);
|
||||
}, 1000);
|
||||
|
||||
return () => window.clearInterval(intervalId);
|
||||
}, [isPaused]);
|
||||
|
||||
function canSeeContact(contact) {
|
||||
if (isAdminUser) {
|
||||
@ -313,7 +391,15 @@ export function useChat() {
|
||||
return currentUserAreas.includes(contact.assignment.area_nome);
|
||||
}
|
||||
|
||||
async function loadChats({ showLoading = false } = {}) {
|
||||
async function loadChats({ showLoading = false, ignorePause = false } = {}) {
|
||||
if (isPaused && !ignorePause) {
|
||||
setContacts((current) => (current.length ? [] : current));
|
||||
setActiveContactId('');
|
||||
setMessagesByContact({});
|
||||
setIsLoadingChats(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whatsappStatus !== 'CONNECTED') {
|
||||
setContacts((current) => (current.length ? [] : current));
|
||||
setActiveContactId('');
|
||||
@ -358,7 +444,7 @@ export function useChat() {
|
||||
isMounted = false;
|
||||
window.clearInterval(intervalId);
|
||||
};
|
||||
}, [currentUserId, currentUserAreas.join('|'), isAdminUser, whatsappStatus]);
|
||||
}, [currentUserId, currentUserAreas.join('|'), isAdminUser, isPaused, whatsappStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeContactId) return;
|
||||
@ -586,6 +672,43 @@ export function useChat() {
|
||||
}));
|
||||
}
|
||||
|
||||
async function pauseAttendance() {
|
||||
if (!currentUserId) return;
|
||||
setIsPresenceLoading(true);
|
||||
try {
|
||||
const result = await pauseAgent(currentUserId);
|
||||
setAgentPresence(result.presence);
|
||||
setPauseSeconds(0);
|
||||
setContacts([]);
|
||||
setActiveContactId('');
|
||||
setMessagesByContact({});
|
||||
setIsTransferOpen(false);
|
||||
setApiError(null);
|
||||
} catch (error) {
|
||||
setApiError(error.message);
|
||||
} finally {
|
||||
setIsPresenceLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function resumeAttendance() {
|
||||
if (!currentUserId) return;
|
||||
setIsPresenceLoading(true);
|
||||
try {
|
||||
const result = await resumeAgent(currentUserId);
|
||||
setAgentPresence(result.presence);
|
||||
setPauseSeconds(0);
|
||||
setApiError(null);
|
||||
await loadChats({ showLoading: true, ignorePause: true });
|
||||
const presences = await listAgentPresence();
|
||||
setPresenceList(Array.isArray(presences) ? presences : []);
|
||||
} catch (error) {
|
||||
setApiError(error.message);
|
||||
} finally {
|
||||
setIsPresenceLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage(messageText = draft, contactId = activeContactId) {
|
||||
const rawMessage = typeof messageText === 'string' ? messageText : draft;
|
||||
const trimmed = rawMessage.trim();
|
||||
@ -769,5 +892,12 @@ export function useChat() {
|
||||
transferNote,
|
||||
setTransferNote,
|
||||
submitTransfer,
|
||||
agentPresence,
|
||||
isPaused,
|
||||
pauseSeconds,
|
||||
pauseDurationLabel: formatPauseDuration(pauseSeconds),
|
||||
isPresenceLoading,
|
||||
pauseAttendance,
|
||||
resumeAttendance,
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,6 +48,8 @@ export function ChatPage() {
|
||||
transferNote,
|
||||
setTransferNote,
|
||||
submitTransfer,
|
||||
isPaused,
|
||||
pauseDurationLabel,
|
||||
} = useChat();
|
||||
const requestedChatId = searchParams.get('chatId');
|
||||
const handledRequestedChatIdRef = useRef('');
|
||||
@ -111,7 +113,7 @@ export function ChatPage() {
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Atendimento em tempo real
|
||||
{isPaused ? `Pausado ha ${pauseDurationLabel}` : 'Atendimento em tempo real'}
|
||||
</div>
|
||||
<Link
|
||||
to="/home"
|
||||
@ -173,6 +175,8 @@ export function ChatPage() {
|
||||
assignmentLabel={assignmentLabel}
|
||||
transferNote={transferNoteLabel}
|
||||
isReplying={isReplying}
|
||||
isPaused={isPaused}
|
||||
pauseDurationLabel={pauseDurationLabel}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
|
||||
@ -188,6 +192,7 @@ export function ChatPage() {
|
||||
key={reply}
|
||||
type="button"
|
||||
onClick={() => setDraft(reply)}
|
||||
disabled={isPaused}
|
||||
style={{
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '18px',
|
||||
@ -196,6 +201,7 @@ export function ChatPage() {
|
||||
color: 'var(--color-primary)',
|
||||
fontWeight: 600,
|
||||
textAlign: 'left',
|
||||
opacity: isPaused ? 0.55 : 1,
|
||||
}}
|
||||
>
|
||||
{reply}
|
||||
|
||||
46
src/modules/chat/services/agentPresenceService.js
Normal file
46
src/modules/chat/services/agentPresenceService.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { API_BASE_URL } from '../../../shared/services/apiConfig';
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Falha ao atualizar presenca do agente');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getAgentPresence(userId) {
|
||||
return request(`/agent/presence/me?userId=${encodeURIComponent(userId)}`);
|
||||
}
|
||||
|
||||
export async function listAgentPresence() {
|
||||
return request('/agent/presence');
|
||||
}
|
||||
|
||||
export async function pauseAgent(userId) {
|
||||
return request('/agent/presence/pause', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ userId }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function resumeAgent(userId) {
|
||||
return request('/agent/presence/resume', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ userId }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function markAgentOffline(userId) {
|
||||
return request('/agent/presence/offline', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ userId }),
|
||||
});
|
||||
}
|
||||
@ -1,29 +1,39 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function AttendantOpsPanel({ activeChatsCount }) {
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
export function AttendantOpsPanel({
|
||||
activeChatsCount,
|
||||
isPaused = false,
|
||||
pauseDurationLabel = '00:00',
|
||||
isPresenceLoading = false,
|
||||
onTogglePause,
|
||||
}) {
|
||||
const [secondsOnline, setSecondsOnline] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
let interval;
|
||||
if (!isPaused) {
|
||||
interval = setInterval(() => {
|
||||
setSecondsOnline((s) => s + 1);
|
||||
}, 1000);
|
||||
}
|
||||
return () => clearInterval(interval);
|
||||
if (isPaused) return undefined;
|
||||
|
||||
const intervalId = window.setInterval(() => {
|
||||
setSecondsOnline((current) => current + 1);
|
||||
}, 1000);
|
||||
|
||||
return () => window.clearInterval(intervalId);
|
||||
}, [isPaused]);
|
||||
|
||||
const formatTime = (totalSeconds) => {
|
||||
const h = Math.floor(totalSeconds / 3600);
|
||||
const m = Math.floor((totalSeconds % 3600) / 60);
|
||||
const s = totalSeconds % 60;
|
||||
return [h, m, s]
|
||||
.map(v => v.toString().padStart(2, '0'))
|
||||
.filter((v, i) => v !== '00' || i > 0)
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
return [hours, minutes, seconds]
|
||||
.map((value) => value.toString().padStart(2, '0'))
|
||||
.filter((value, index) => value !== '00' || index > 0)
|
||||
.join(':');
|
||||
};
|
||||
|
||||
const presenceLabel = isPaused ? 'Tempo em pausa' : 'Tempo online';
|
||||
const presenceTime = isPaused ? pauseDurationLabel : formatTime(secondsOnline);
|
||||
const statusColor = isPaused ? '#ef4444' : '#10b981';
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@ -46,20 +56,22 @@ export function AttendantOpsPanel({ activeChatsCount }) {
|
||||
>
|
||||
<div>
|
||||
<span style={{ color: 'var(--color-text-soft)', fontSize: '0.9rem', fontWeight: 600 }}>
|
||||
Tempo Online
|
||||
{presenceLabel}
|
||||
</span>
|
||||
<strong style={{ display: 'block', fontSize: '1.6rem', marginTop: '0.2rem', color: 'var(--color-text)' }}>
|
||||
{formatTime(secondsOnline)}
|
||||
{presenceTime}
|
||||
</strong>
|
||||
</div>
|
||||
<div style={{
|
||||
width: '12px',
|
||||
height: '12px',
|
||||
borderRadius: '50%',
|
||||
background: isPaused ? '#ef4444' : '#10b981',
|
||||
boxShadow: `0 0 10px ${isPaused ? '#ef4444' : '#10b981'}`,
|
||||
animation: !isPaused ? 'pulse 2s infinite' : 'none'
|
||||
}} />
|
||||
<div
|
||||
title={isPaused ? 'Agente pausado' : 'Agente disponivel'}
|
||||
style={{
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: '50%',
|
||||
background: statusColor,
|
||||
boxShadow: `0 0 10px ${statusColor}`,
|
||||
}}
|
||||
/>
|
||||
</article>
|
||||
|
||||
<article
|
||||
@ -76,10 +88,10 @@ export function AttendantOpsPanel({ activeChatsCount }) {
|
||||
>
|
||||
<div>
|
||||
<span style={{ color: 'var(--color-text-soft)', fontSize: '0.9rem', fontWeight: 600 }}>
|
||||
Atendimentos Abertos
|
||||
Atendimentos abertos
|
||||
</span>
|
||||
<strong style={{ display: 'block', fontSize: '1.6rem', marginTop: '0.2rem', color: 'var(--color-text)' }}>
|
||||
{activeChatsCount}
|
||||
{isPaused ? 0 : activeChatsCount}
|
||||
</strong>
|
||||
</div>
|
||||
</article>
|
||||
@ -93,11 +105,13 @@ export function AttendantOpsPanel({ activeChatsCount }) {
|
||||
boxShadow: 'var(--shadow-sm)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsPaused(!isPaused)}
|
||||
type="button"
|
||||
onClick={onTogglePause}
|
||||
disabled={isPresenceLoading}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@ -107,12 +121,13 @@ export function AttendantOpsPanel({ activeChatsCount }) {
|
||||
background: isPaused ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
|
||||
color: isPaused ? '#10b981' : '#ef4444',
|
||||
fontSize: '1rem',
|
||||
fontWeight: 700,
|
||||
cursor: 'pointer',
|
||||
fontWeight: 800,
|
||||
cursor: isPresenceLoading ? 'wait' : 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
opacity: isPresenceLoading ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{isPaused ? '▶ Retomar Atendimento' : '⏸ Pausar'}
|
||||
{isPaused ? 'Retomar Atendimento' : 'Pausar'}
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@ -200,6 +200,8 @@ export function MessagesWorkspace({
|
||||
isDesktop = false,
|
||||
isTablet = false,
|
||||
isMobile = false,
|
||||
isPaused = false,
|
||||
pauseDurationLabel = '00:00',
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const messagesRef = useRef(null);
|
||||
@ -311,6 +313,7 @@ export function MessagesWorkspace({
|
||||
}
|
||||
|
||||
async function sendSuggestedReply() {
|
||||
if (isPaused) return;
|
||||
if (!safeActiveConversation.id || safeActiveConversation.id === 'empty') return;
|
||||
|
||||
await onSendSuggestedReply?.(safeActiveConversation.id, selectedReply);
|
||||
@ -393,7 +396,10 @@ export function MessagesWorkspace({
|
||||
{conversations.length > 3 ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/chat')}
|
||||
onClick={() => {
|
||||
if (!isPaused) navigate('/chat');
|
||||
}}
|
||||
disabled={isPaused}
|
||||
style={{
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '16px',
|
||||
@ -401,6 +407,8 @@ export function MessagesWorkspace({
|
||||
background: '#fff',
|
||||
color: 'var(--color-primary)',
|
||||
fontWeight: 700,
|
||||
opacity: isPaused ? 0.55 : 1,
|
||||
cursor: isPaused ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
>
|
||||
Ver todos no chat
|
||||
@ -442,7 +450,10 @@ export function MessagesWorkspace({
|
||||
<div style={{ display: 'flex', gap: '0.6rem', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/chat')}
|
||||
onClick={() => {
|
||||
if (!isPaused) navigate('/chat');
|
||||
}}
|
||||
disabled={isPaused}
|
||||
style={{
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '14px',
|
||||
@ -450,6 +461,8 @@ export function MessagesWorkspace({
|
||||
background: '#fff',
|
||||
color: 'var(--color-primary)',
|
||||
fontWeight: 700,
|
||||
opacity: isPaused ? 0.55 : 1,
|
||||
cursor: isPaused ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
>
|
||||
Abrir chat
|
||||
@ -571,12 +584,15 @@ export function MessagesWorkspace({
|
||||
type="button"
|
||||
onClick={selectPreviousReply}
|
||||
title="Resposta anterior"
|
||||
disabled={isPaused}
|
||||
style={{
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '14px',
|
||||
background: '#fff',
|
||||
color: 'var(--color-primary)',
|
||||
fontWeight: 900,
|
||||
opacity: isPaused ? 0.55 : 1,
|
||||
cursor: isPaused ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
>
|
||||
‹
|
||||
@ -584,6 +600,7 @@ export function MessagesWorkspace({
|
||||
<button
|
||||
type="button"
|
||||
onClick={sendSuggestedReply}
|
||||
disabled={isPaused}
|
||||
style={{
|
||||
border: '1px solid rgba(0, 164, 183, 0.32)',
|
||||
borderRadius: '16px',
|
||||
@ -598,9 +615,11 @@ export function MessagesWorkspace({
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
opacity: isPaused ? 0.55 : 1,
|
||||
cursor: isPaused ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
>
|
||||
{selectedReply}
|
||||
{isPaused ? `Voce esta em pausa ha ${pauseDurationLabel}. Retome para responder.` : selectedReply}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@ -37,6 +37,11 @@ export function HomePage() {
|
||||
messages,
|
||||
sendMessage,
|
||||
isLoadingChats,
|
||||
isPaused,
|
||||
pauseDurationLabel,
|
||||
isPresenceLoading,
|
||||
pauseAttendance,
|
||||
resumeAttendance,
|
||||
} = useChat();
|
||||
const [activeTab, setActiveTab] = useState('messages');
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
@ -122,7 +127,13 @@ export function HomePage() {
|
||||
gap: '1rem',
|
||||
}}
|
||||
>
|
||||
<AttendantOpsPanel activeChatsCount={filteredConversations.length} />
|
||||
<AttendantOpsPanel
|
||||
activeChatsCount={filteredConversations.length}
|
||||
isPaused={isPaused}
|
||||
pauseDurationLabel={pauseDurationLabel}
|
||||
isPresenceLoading={isPresenceLoading}
|
||||
onTogglePause={isPaused ? resumeAttendance : pauseAttendance}
|
||||
/>
|
||||
|
||||
{isLoadingChats ? (
|
||||
<div
|
||||
@ -152,6 +163,8 @@ export function HomePage() {
|
||||
isDesktop={isDesktop}
|
||||
isTablet={isTablet}
|
||||
isMobile={isMobile}
|
||||
isPaused={isPaused}
|
||||
pauseDurationLabel={pauseDurationLabel}
|
||||
/>
|
||||
) : (
|
||||
<CallsWorkspace
|
||||
|
||||
Loading…
Reference in New Issue
Block a user