FEAT: Adicionado canais e integracoes no painel administrativo

This commit is contained in:
Rafael Alves Lopes 2026-05-26 11:35:23 -03:00
parent 751038be0f
commit 072cd1cb75

View File

@ -58,6 +58,49 @@ const initialNotices = [
{ id: 'n2', text: 'Templates de abertura ativa atualizados para WhatsApp.' },
];
const integrationCards = [
{
id: 'whatsapp',
group: 'Canal',
name: 'WhatsApp',
icon: 'WA',
color: '#20a45b',
description: 'Canal principal para atendimento, abertura ativa e continuidade das conversas no chat.',
},
{
id: 'sms',
group: 'Canal',
name: 'SMS',
icon: 'SM',
color: '#00a4b7',
description: 'Envio de comunicados curtos, confirmações e mensagens transacionais para contatos sem WhatsApp.',
},
{
id: 'email',
group: 'Canal',
name: 'Email',
icon: 'EM',
color: '#d8891c',
description: 'Recebimento e resposta de demandas por email dentro da fila omnichannel.',
},
{
id: 'sharepoint',
group: 'Integração',
name: 'SharePoint',
icon: 'SP',
color: '#036c70',
description: 'Permite que a IA visualize documentos autorizados para alimentar e manter a base de conhecimento.',
},
{
id: 'gupy',
group: 'Integração',
name: 'Gupy',
icon: 'GP',
color: '#7b4cc2',
description: 'Conecta vagas abertas e processos dos candidatos para enriquecer a base de conhecimento.',
},
];
function formatMinutes(minutes) {
if (minutes === null || minutes === undefined || Number.isNaN(Number(minutes))) return 'Sem dados';
return `${Number(minutes)} min`;
@ -163,6 +206,13 @@ export function AdminPage() {
const [editUserProfileId, setEditUserProfileId] = useState('');
const [editUserSpecialties, setEditUserSpecialties] = useState([]);
const [specialtyToAdd, setSpecialtyToAdd] = useState('');
const [integrationStates, setIntegrationStates] = useState({
whatsapp: true,
sms: false,
email: false,
sharepoint: false,
gupy: false,
});
useEffect(() => {
let isMounted = true;
@ -1216,6 +1266,186 @@ export function AdminPage() {
);
}
function renderChannelsIntegrations() {
const activeCount = integrationCards.filter((item) => integrationStates[item.id]).length;
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;
return (
<section style={{ display: 'grid', gap: '1rem' }}>
<DataPanel
title="Canais e Integrações"
description="Controle quais canais ficam disponíveis e quais integrações alimentam a operação e a IA."
>
<div
style={{
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, minmax(0, 1fr))',
gap: '0.8rem',
}}
>
{[
{ label: 'Ativos', value: activeCount },
{ label: 'Canais habilitados', value: channelCount },
{ label: 'Integrações habilitadas', value: integrationCount },
].map((item) => (
<div
key={item.label}
style={{
border: '1px solid var(--color-border)',
borderRadius: 18,
padding: '1rem',
background: '#f8fbfc',
display: 'grid',
gap: '0.25rem',
}}
>
<span style={{ color: 'var(--color-text-soft)', fontWeight: 700 }}>{item.label}</span>
<strong style={{ fontSize: '1.55rem' }}>{item.value}</strong>
</div>
))}
</div>
</DataPanel>
<div
style={{
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : isTablet ? 'repeat(2, minmax(0, 1fr))' : 'repeat(3, minmax(0, 1fr))',
gap: '1rem',
}}
>
{integrationCards.map((item) => {
const isEnabled = Boolean(integrationStates[item.id]);
return (
<article
key={item.id}
style={{
border: `1px solid ${isEnabled ? `${item.color}55` : 'var(--color-border)'}`,
borderRadius: 22,
padding: '1.1rem',
background: isEnabled ? `${item.color}0f` : '#fff',
display: 'grid',
gap: '1rem',
minHeight: 260,
}}
>
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: '0.85rem' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.8rem', minWidth: 0 }}>
<div
aria-hidden="true"
style={{
width: 52,
height: 52,
borderRadius: 16,
display: 'grid',
placeItems: 'center',
background: item.color,
color: '#fff',
fontWeight: 900,
letterSpacing: 0,
boxShadow: `0 12px 22px ${item.color}24`,
flex: '0 0 auto',
}}
>
{item.icon}
</div>
<div style={{ minWidth: 0 }}>
<span
style={{
display: 'inline-flex',
borderRadius: 999,
padding: '0.18rem 0.55rem',
background: `${item.color}18`,
color: item.color,
fontSize: '0.78rem',
fontWeight: 800,
}}
>
{item.group}
</span>
<strong style={{ display: 'block', marginTop: '0.35rem', fontSize: '1.15rem' }}>{item.name}</strong>
</div>
</div>
<button
type="button"
aria-pressed={isEnabled}
onClick={() => {
setIntegrationStates((current) => ({
...current,
[item.id]: !current[item.id],
}));
}}
style={{
border: 'none',
borderRadius: 999,
width: 54,
height: 30,
padding: 3,
background: isEnabled ? item.color : '#d6e0e5',
display: 'flex',
alignItems: 'center',
justifyContent: isEnabled ? 'flex-end' : 'flex-start',
cursor: 'pointer',
flex: '0 0 auto',
}}
title={isEnabled ? `Desabilitar ${item.name}` : `Habilitar ${item.name}`}
>
<span
aria-hidden="true"
style={{
width: 24,
height: 24,
borderRadius: '50%',
background: '#fff',
boxShadow: '0 3px 8px rgba(0, 0, 0, 0.18)',
}}
/>
</button>
</div>
<p style={{ margin: 0, color: 'var(--color-text-soft)', lineHeight: 1.5, fontWeight: 650 }}>
{item.description}
</p>
<div
style={{
marginTop: 'auto',
borderTop: '1px solid var(--color-border)',
paddingTop: '0.85rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: '0.75rem',
flexWrap: 'wrap',
}}
>
<span style={{ color: isEnabled ? item.color : 'var(--color-text-soft)', fontWeight: 800 }}>
{isEnabled ? 'Habilitado' : 'Desabilitado'}
</span>
<button
type="button"
style={{
border: `1px solid ${item.color}44`,
borderRadius: 14,
padding: '0.65rem 0.8rem',
background: '#fff',
color: item.color,
fontWeight: 800,
}}
>
Configurar
</button>
</div>
</article>
);
})}
</div>
</section>
);
}
function renderPlaceholder(title, description) {
return (
<DataPanel title={title} description={description}>
@ -1234,7 +1464,7 @@ export function AdminPage() {
knowledge: <KnowledgeBasePanel areas={areas} mode="admin" isMobile={isMobile} />,
'ai-contents': renderAiContents(),
audit: renderAudit(),
channels: renderPlaceholder('Canais', 'Status e configurações dos canais conectados.'),
channels: renderChannelsIntegrations(),
attendance: (
<AdminAttendanceWorkspace
isWideDesktop={isWideDesktop}
@ -1259,6 +1489,8 @@ export function AdminPage() {
? 'Operação'
: activeAdminSection === 'audit'
? 'Auditoria'
: activeAdminSection === 'channels'
? 'Canais e Integração'
: activeAdminSection === 'ai-contents'
? 'Conteúdos da IA'
: activeAdminSection === 'knowledge'
@ -1275,6 +1507,8 @@ export function AdminPage() {
? 'Indicadores do dia, fila de espera e acompanhamento operacional do time.'
: activeAdminSection === 'audit'
? 'Logs administrativos e operacionais com paginação de 100 eventos.'
: activeAdminSection === 'channels'
? 'Canais de atendimento e integrações que alimentam a operação e a IA.'
: activeAdminSection === 'ai-contents'
? 'Base de documentos que será consultada pela IA em fase de testes.'
: activeAdminSection === 'knowledge'