FEAT: Adicionado configuração para canal de atendimento Whatsapp

This commit is contained in:
Rafael Alves Lopes 2026-05-26 12:12:33 -03:00
parent 072cd1cb75
commit c4b846a079

View File

@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { DataPanel } from '../components/DataPanel';
import { ManagementLayout } from '../components/ManagementLayout';
import { ManagementTable } from '../components/ManagementTable';
@ -65,6 +66,7 @@ const integrationCards = [
name: 'WhatsApp',
icon: 'WA',
color: '#20a45b',
configured: true,
description: 'Canal principal para atendimento, abertura ativa e continuidade das conversas no chat.',
},
{
@ -73,6 +75,7 @@ const integrationCards = [
name: 'SMS',
icon: 'SM',
color: '#00a4b7',
configured: false,
description: 'Envio de comunicados curtos, confirmações e mensagens transacionais para contatos sem WhatsApp.',
},
{
@ -81,6 +84,7 @@ const integrationCards = [
name: 'Email',
icon: 'EM',
color: '#d8891c',
configured: false,
description: 'Recebimento e resposta de demandas por email dentro da fila omnichannel.',
},
{
@ -89,6 +93,7 @@ const integrationCards = [
name: 'SharePoint',
icon: 'SP',
color: '#036c70',
configured: false,
description: 'Permite que a IA visualize documentos autorizados para alimentar e manter a base de conhecimento.',
},
{
@ -97,6 +102,7 @@ const integrationCards = [
name: 'Gupy',
icon: 'GP',
color: '#7b4cc2',
configured: false,
description: 'Conecta vagas abertas e processos dos candidatos para enriquecer a base de conhecimento.',
},
];
@ -180,6 +186,7 @@ export function AdminAttendanceWorkspace({ isWideDesktop, isDesktop, isTablet, i
}
export function AdminPage() {
const navigate = useNavigate();
const { isWideDesktop, isDesktop, isTablet, isMobile } = useViewport();
const userDisplay = getCurrentUserDisplay();
const [activeAdminSection, setActiveAdminSection] = useState('home');
@ -213,6 +220,8 @@ export function AdminPage() {
sharepoint: false,
gupy: false,
});
const [integrationNotice, setIntegrationNotice] = useState('');
const [configurationModal, setConfigurationModal] = useState(null);
useEffect(() => {
let isMounted = true;
@ -1271,6 +1280,21 @@ export function AdminPage() {
const channelCount = integrationCards.filter((item) => item.group === 'Canal' && integrationStates[item.id]).length;
const integrationCount = integrationCards.filter((item) => item.group === 'Integração' && integrationStates[item.id]).length;
function toggleIntegration(item) {
const isEnabled = Boolean(integrationStates[item.id]);
if (!isEnabled && !item.configured) {
setIntegrationNotice(`${item.name} ainda não pode ser habilitado porque precisa ser configurado primeiro.`);
return;
}
setIntegrationNotice('');
setIntegrationStates((current) => ({
...current,
[item.id]: !current[item.id],
}));
}
return (
<section style={{ display: 'grid', gap: '1rem' }}>
<DataPanel
@ -1305,6 +1329,22 @@ export function AdminPage() {
</div>
))}
</div>
{integrationNotice ? (
<div
role="alert"
style={{
border: '1px solid rgba(216, 137, 28, 0.32)',
borderRadius: 16,
padding: '0.85rem 1rem',
background: 'rgba(216, 137, 28, 0.08)',
color: '#7a4a08',
fontWeight: 800,
}}
>
{integrationNotice}
</div>
) : null}
</DataPanel>
<div
@ -1371,12 +1411,7 @@ export function AdminPage() {
<button
type="button"
aria-pressed={isEnabled}
onClick={() => {
setIntegrationStates((current) => ({
...current,
[item.id]: !current[item.id],
}));
}}
onClick={() => toggleIntegration(item)}
style={{
border: 'none',
borderRadius: 999,
@ -1422,10 +1457,18 @@ export function AdminPage() {
}}
>
<span style={{ color: isEnabled ? item.color : 'var(--color-text-soft)', fontWeight: 800 }}>
{isEnabled ? 'Habilitado' : 'Desabilitado'}
{isEnabled ? 'Habilitado' : item.configured ? 'Desabilitado' : 'Pendente de configuração'}
</span>
<button
type="button"
onClick={() => {
if (item.id === 'whatsapp') {
navigate('/admin/whatsapp');
return;
}
setConfigurationModal(item);
}}
style={{
border: `1px solid ${item.color}44`,
borderRadius: 14,
@ -1442,6 +1485,85 @@ export function AdminPage() {
);
})}
</div>
{configurationModal ? (
<div
role="dialog"
aria-modal="true"
aria-labelledby="integration-config-title"
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0, 20, 32, 0.42)',
display: 'grid',
placeItems: 'center',
padding: '1rem',
zIndex: 50,
}}
onClick={() => setConfigurationModal(null)}
>
<div
onClick={(event) => event.stopPropagation()}
style={{
width: 'min(460px, 100%)',
borderRadius: 22,
border: '1px solid var(--color-border)',
background: '#fff',
padding: '1.25rem',
display: 'grid',
gap: '0.9rem',
boxShadow: 'var(--shadow-lg)',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.8rem' }}>
<div
aria-hidden="true"
style={{
width: 44,
height: 44,
borderRadius: 14,
display: 'grid',
placeItems: 'center',
background: configurationModal.color,
color: '#fff',
fontWeight: 900,
}}
>
{configurationModal.icon}
</div>
<div>
<strong id="integration-config-title" style={{ display: 'block', fontSize: '1.08rem' }}>
Configurar {configurationModal.name}
</strong>
<span style={{ color: 'var(--color-text-soft)', fontWeight: 700 }}>
{configurationModal.group}
</span>
</div>
</div>
<p style={{ margin: 0, color: 'var(--color-text-soft)', lineHeight: 1.5, fontWeight: 700 }}>
Esta configuração ainda está em construção. Assim que a integração estiver disponível, este espaço vai reunir credenciais, permissões e parâmetros de sincronização.
</p>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<button
type="button"
onClick={() => setConfigurationModal(null)}
style={{
border: 'none',
borderRadius: 14,
padding: '0.75rem 0.95rem',
background: 'var(--color-primary)',
color: '#fff',
fontWeight: 800,
}}
>
Entendi
</button>
</div>
</div>
</div>
) : null}
</section>
);
}