- adiciona flow builder visual, conteúdos da IA e disparo em massa com agenda - melhora templates com categoria, variáveis e preview estilo WhatsApp - ajusta abrir atendimento dentro do painel, com preview, tag e variáveis - refina tela de chat com encerramento, status de fila e correções visuais
269 lines
8.5 KiB
JavaScript
269 lines
8.5 KiB
JavaScript
import { Link, useSearchParams } from 'react-router-dom';
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import { BrandMark } from '../../../shared/components/BrandMark';
|
|
import { useViewport } from '../../../shared/hooks/useViewport';
|
|
import { ChatConversationList } from '../components/ChatConversationList';
|
|
import { ChatTransferPanel } from '../components/ChatTransferPanel';
|
|
import { ContactProfilePanel } from '../components/ContactProfilePanel';
|
|
import { ChatWindow } from '../components/ChatWindow';
|
|
import { useChat } from '../hooks/useChat';
|
|
import { quickReplies } from '../services/chatMocks';
|
|
|
|
export function ChatPage() {
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
const { isWideDesktop, isDesktop, isTablet, isMobile } = useViewport();
|
|
const {
|
|
currentUserId,
|
|
contacts,
|
|
activeContact,
|
|
activeContactId,
|
|
setActiveContactId,
|
|
messages,
|
|
draft,
|
|
setDraft,
|
|
attachedFile,
|
|
attachFile,
|
|
removeAttachedFile,
|
|
sendMessage,
|
|
hydrateMessageMedia,
|
|
assumeChat,
|
|
releaseChat,
|
|
closeChat,
|
|
canAssumeChat,
|
|
canReply,
|
|
assignmentLabel,
|
|
transferNoteLabel,
|
|
updateContactProfile,
|
|
isReplying,
|
|
selectedArea,
|
|
setSelectedArea,
|
|
isTransferOpen,
|
|
setIsTransferOpen,
|
|
transferArea,
|
|
setTransferArea,
|
|
transferAreas,
|
|
attendants,
|
|
isSameUserArea,
|
|
transferAttendant,
|
|
setTransferAttendant,
|
|
transferNote,
|
|
setTransferNote,
|
|
submitTransfer,
|
|
isPaused,
|
|
pauseDurationLabel,
|
|
} = useChat();
|
|
const requestedChatId = searchParams.get('chatId');
|
|
const handledRequestedChatIdRef = useRef('');
|
|
const [isContactPanelOpen, setIsContactPanelOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!requestedChatId) return;
|
|
if (handledRequestedChatIdRef.current === requestedChatId) return;
|
|
if (!contacts.some((contact) => contact.id === requestedChatId)) return;
|
|
handledRequestedChatIdRef.current = requestedChatId;
|
|
setActiveContactId(requestedChatId);
|
|
setSearchParams({}, { replace: true });
|
|
}, [requestedChatId, contacts, setActiveContactId, setSearchParams]);
|
|
|
|
function selectContact(contactId) {
|
|
setActiveContactId(contactId);
|
|
if (requestedChatId) {
|
|
setSearchParams({}, { replace: true });
|
|
}
|
|
}
|
|
|
|
const gridTemplateColumns = isMobile
|
|
? '1fr'
|
|
: isWideDesktop
|
|
? 'minmax(260px, 320px) minmax(0, 1.6fr) minmax(280px, 320px)'
|
|
: isDesktop || isTablet
|
|
? 'minmax(260px, 320px) minmax(0, 1fr)'
|
|
: '1fr';
|
|
|
|
return (
|
|
<main style={{ minHeight: '100vh', padding: '1.5rem' }}>
|
|
<section
|
|
style={{
|
|
width: 'min(1680px, calc(100vw - 3rem))',
|
|
margin: '0 auto',
|
|
background: 'var(--color-surface-strong)',
|
|
borderRadius: '32px',
|
|
boxShadow: 'var(--shadow-lg)',
|
|
padding: '1.5rem',
|
|
display: 'grid',
|
|
gap: '1.25rem',
|
|
}}
|
|
>
|
|
<header
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: isMobile ? '1fr' : 'auto 1fr auto',
|
|
gap: '1rem',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<BrandMark />
|
|
<div
|
|
style={{
|
|
justifySelf: isMobile ? 'stretch' : 'center',
|
|
padding: '0.85rem 1rem',
|
|
borderRadius: '18px',
|
|
background: 'rgba(0, 49, 80, 0.06)',
|
|
color: 'var(--color-primary)',
|
|
fontWeight: 700,
|
|
textAlign: 'center',
|
|
}}
|
|
>
|
|
{isPaused ? `Pausado ha ${pauseDurationLabel}` : 'Atendimento em tempo real'}
|
|
</div>
|
|
<Link
|
|
to="/home"
|
|
style={{
|
|
justifySelf: isMobile ? 'stretch' : 'end',
|
|
borderRadius: '16px',
|
|
padding: '0.85rem 1rem',
|
|
background: 'var(--color-primary)',
|
|
color: '#fff',
|
|
fontWeight: 700,
|
|
textAlign: 'center',
|
|
}}
|
|
>
|
|
Voltar para home
|
|
</Link>
|
|
</header>
|
|
|
|
<section
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns,
|
|
gap: '1rem',
|
|
alignItems: 'stretch',
|
|
}}
|
|
>
|
|
<ChatConversationList
|
|
contacts={contacts}
|
|
activeContactId={activeContactId}
|
|
onSelectContact={selectContact}
|
|
onOpenContact={() => {
|
|
setIsTransferOpen(false);
|
|
setIsContactPanelOpen(true);
|
|
}}
|
|
currentUserId={currentUserId}
|
|
isMobile={isMobile}
|
|
/>
|
|
|
|
<div style={{ display: 'grid', gap: '1rem', minWidth: 0, alignContent: 'start' }}>
|
|
<ChatWindow
|
|
contact={activeContact}
|
|
messages={messages}
|
|
selectedArea={selectedArea}
|
|
setSelectedArea={setSelectedArea}
|
|
draft={draft}
|
|
setDraft={setDraft}
|
|
attachedFile={attachedFile}
|
|
onAttachFile={attachFile}
|
|
onRemoveAttachedFile={removeAttachedFile}
|
|
onLoadMedia={hydrateMessageMedia}
|
|
onSend={sendMessage}
|
|
onToggleTransfer={() => {
|
|
setIsContactPanelOpen(false);
|
|
setIsTransferOpen((current) => !current);
|
|
}}
|
|
onAssumeChat={assumeChat}
|
|
onReleaseChat={releaseChat}
|
|
onCloseChat={closeChat}
|
|
canAssumeChat={canAssumeChat}
|
|
canReply={canReply}
|
|
assignmentLabel={assignmentLabel}
|
|
transferNote={transferNoteLabel}
|
|
isReplying={isReplying}
|
|
isPaused={isPaused}
|
|
pauseDurationLabel={pauseDurationLabel}
|
|
isMobile={isMobile}
|
|
/>
|
|
|
|
<div
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
|
|
gap: '0.75rem',
|
|
}}
|
|
>
|
|
{quickReplies.map((reply) => (
|
|
<button
|
|
key={reply}
|
|
type="button"
|
|
onClick={() => setDraft(reply)}
|
|
disabled={isPaused}
|
|
style={{
|
|
border: '1px solid var(--color-border)',
|
|
borderRadius: '18px',
|
|
padding: '0.85rem 1rem',
|
|
background: '#fff',
|
|
color: 'var(--color-primary)',
|
|
fontWeight: 600,
|
|
textAlign: 'left',
|
|
opacity: isPaused ? 0.55 : 1,
|
|
}}
|
|
>
|
|
{reply}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{isWideDesktop ? (
|
|
<>
|
|
<ChatTransferPanel
|
|
isOpen={isTransferOpen}
|
|
transferArea={transferArea}
|
|
setTransferArea={setTransferArea}
|
|
transferAreas={transferAreas}
|
|
attendants={attendants}
|
|
isSameUserArea={isSameUserArea}
|
|
transferAttendant={transferAttendant}
|
|
setTransferAttendant={setTransferAttendant}
|
|
transferNote={transferNote}
|
|
setTransferNote={setTransferNote}
|
|
onSubmit={submitTransfer}
|
|
onClose={() => setIsTransferOpen(false)}
|
|
/>
|
|
<ContactProfilePanel
|
|
isOpen={isContactPanelOpen}
|
|
contact={activeContact}
|
|
onClose={() => setIsContactPanelOpen(false)}
|
|
onSaved={updateContactProfile}
|
|
/>
|
|
</>
|
|
) : null}
|
|
</section>
|
|
|
|
{!isWideDesktop ? (
|
|
<>
|
|
<ChatTransferPanel
|
|
isOpen={isTransferOpen}
|
|
transferArea={transferArea}
|
|
setTransferArea={setTransferArea}
|
|
transferAreas={transferAreas}
|
|
attendants={attendants}
|
|
isSameUserArea={isSameUserArea}
|
|
transferAttendant={transferAttendant}
|
|
setTransferAttendant={setTransferAttendant}
|
|
transferNote={transferNote}
|
|
setTransferNote={setTransferNote}
|
|
onSubmit={submitTransfer}
|
|
onClose={() => setIsTransferOpen(false)}
|
|
/>
|
|
<ContactProfilePanel
|
|
isOpen={isContactPanelOpen}
|
|
contact={activeContact}
|
|
onClose={() => setIsContactPanelOpen(false)}
|
|
onSaved={updateContactProfile}
|
|
/>
|
|
</>
|
|
) : null}
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|