FEAT: Adicionado canais e integracoes no painel administrativo
This commit is contained in:
parent
751038be0f
commit
072cd1cb75
@ -58,6 +58,49 @@ const initialNotices = [
|
|||||||
{ id: 'n2', text: 'Templates de abertura ativa atualizados para WhatsApp.' },
|
{ 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) {
|
function formatMinutes(minutes) {
|
||||||
if (minutes === null || minutes === undefined || Number.isNaN(Number(minutes))) return 'Sem dados';
|
if (minutes === null || minutes === undefined || Number.isNaN(Number(minutes))) return 'Sem dados';
|
||||||
return `${Number(minutes)} min`;
|
return `${Number(minutes)} min`;
|
||||||
@ -163,6 +206,13 @@ export function AdminPage() {
|
|||||||
const [editUserProfileId, setEditUserProfileId] = useState('');
|
const [editUserProfileId, setEditUserProfileId] = useState('');
|
||||||
const [editUserSpecialties, setEditUserSpecialties] = useState([]);
|
const [editUserSpecialties, setEditUserSpecialties] = useState([]);
|
||||||
const [specialtyToAdd, setSpecialtyToAdd] = useState('');
|
const [specialtyToAdd, setSpecialtyToAdd] = useState('');
|
||||||
|
const [integrationStates, setIntegrationStates] = useState({
|
||||||
|
whatsapp: true,
|
||||||
|
sms: false,
|
||||||
|
email: false,
|
||||||
|
sharepoint: false,
|
||||||
|
gupy: false,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
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) {
|
function renderPlaceholder(title, description) {
|
||||||
return (
|
return (
|
||||||
<DataPanel title={title} description={description}>
|
<DataPanel title={title} description={description}>
|
||||||
@ -1234,7 +1464,7 @@ export function AdminPage() {
|
|||||||
knowledge: <KnowledgeBasePanel areas={areas} mode="admin" isMobile={isMobile} />,
|
knowledge: <KnowledgeBasePanel areas={areas} mode="admin" isMobile={isMobile} />,
|
||||||
'ai-contents': renderAiContents(),
|
'ai-contents': renderAiContents(),
|
||||||
audit: renderAudit(),
|
audit: renderAudit(),
|
||||||
channels: renderPlaceholder('Canais', 'Status e configurações dos canais conectados.'),
|
channels: renderChannelsIntegrations(),
|
||||||
attendance: (
|
attendance: (
|
||||||
<AdminAttendanceWorkspace
|
<AdminAttendanceWorkspace
|
||||||
isWideDesktop={isWideDesktop}
|
isWideDesktop={isWideDesktop}
|
||||||
@ -1259,6 +1489,8 @@ export function AdminPage() {
|
|||||||
? 'Operação'
|
? 'Operação'
|
||||||
: activeAdminSection === 'audit'
|
: activeAdminSection === 'audit'
|
||||||
? 'Auditoria'
|
? 'Auditoria'
|
||||||
|
: activeAdminSection === 'channels'
|
||||||
|
? 'Canais e Integração'
|
||||||
: activeAdminSection === 'ai-contents'
|
: activeAdminSection === 'ai-contents'
|
||||||
? 'Conteúdos da IA'
|
? 'Conteúdos da IA'
|
||||||
: activeAdminSection === 'knowledge'
|
: activeAdminSection === 'knowledge'
|
||||||
@ -1275,6 +1507,8 @@ export function AdminPage() {
|
|||||||
? 'Indicadores do dia, fila de espera e acompanhamento operacional do time.'
|
? 'Indicadores do dia, fila de espera e acompanhamento operacional do time.'
|
||||||
: activeAdminSection === 'audit'
|
: activeAdminSection === 'audit'
|
||||||
? 'Logs administrativos e operacionais com paginação de 100 eventos.'
|
? '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'
|
: activeAdminSection === 'ai-contents'
|
||||||
? 'Base de documentos que será consultada pela IA em fase de testes.'
|
? 'Base de documentos que será consultada pela IA em fase de testes.'
|
||||||
: activeAdminSection === 'knowledge'
|
: activeAdminSection === 'knowledge'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user