omnichannel-frontend/src/modules/management/components/ManagementLayout.jsx

277 lines
9.1 KiB
JavaScript

import { useNavigate } from 'react-router-dom';
import { BrandMark } from '../../../shared/components/BrandMark';
import { clearSession } from '../../auth/services/sessionService';
const navigationBySection = {
supervisor: [
{ id: 'dashboard', label: 'Home' },
{ id: 'templates', label: 'Templates' },
{ id: 'knowledge', label: 'Base de conhecimento IA' },
{ id: 'audit', label: 'Auditoria' },
{ type: 'separator' },
{ id: 'attendance', label: 'Atendimento' },
{ id: 'new-attendance', label: 'Abrir Atendimento', path: '/new-attendance' },
{ id: 'mass-message', label: 'Disparo em Massa' },
{ id: 'contacts', label: 'Contatos' },
],
admin: [
{ id: 'home', label: 'Home' },
{ id: 'today', label: 'Operação' },
{ type: 'separator' },
{ id: 'users-access', label: 'Usuários & Acessos' },
{ id: 'templates', label: 'Templates' },
{ id: 'knowledge', label: 'Base de conhecimento IA' },
{ id: 'audit', label: 'Auditoria' },
{ id: 'channels', label: 'Canais' },
{ type: 'separator' },
{ id: 'attendance', label: 'Atendimento' },
{ id: 'new-attendance', label: 'Abrir Atendimento', path: '/new-attendance' },
{ id: 'mass-message', label: 'Disparo em Massa' },
{ id: 'contacts', label: 'Contatos' },
{ type: 'separator' },
{ id: 'settings', label: 'Configurações' },
],
};
const actionLabelBySection = {
supervisor: '+ Redistribuir atendimento',
admin: 'Home',
};
export function ManagementLayout({
title,
subtitle,
activeSection,
profileLabel,
initials,
children,
isDesktop,
isMobile,
activeNavItem,
onNavItemChange,
}) {
const navigate = useNavigate();
const navItems = navigationBySection[activeSection] || navigationBySection.supervisor;
const actionLabel = actionLabelBySection[activeSection] || 'Home';
function handleLogout() {
clearSession();
navigate('/login', { replace: true });
}
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.5rem',
}}
>
<div
style={{
display: 'grid',
gridTemplateColumns: isDesktop ? 'minmax(300px, 360px) minmax(0, 1fr)' : '1fr',
gap: '1.5rem',
alignItems: 'start',
}}
>
<div style={{ display: 'grid', gap: '1.25rem' }}>
<div
style={{
background: '#fff',
border: '1px solid var(--color-border)',
borderRadius: '28px',
padding: '1.5rem',
}}
>
<BrandMark size="lg" />
</div>
<aside
style={{
background: 'linear-gradient(180deg, rgba(0, 49, 80, 0.98), rgba(7, 64, 98, 0.96))',
color: '#fff',
borderRadius: '28px',
padding: '1.5rem',
display: 'grid',
gap: '1.25rem',
alignContent: 'start',
}}
>
<button
type="button"
onClick={() => navigate('/home')}
style={{
border: 'none',
borderRadius: '20px',
padding: '1rem 1.15rem',
background: 'linear-gradient(135deg, var(--color-highlight), #f3b94d)',
color: '#132534',
fontWeight: 800,
textAlign: 'left',
}}
>
{actionLabel}
</button>
<nav
style={{
display: 'grid',
gap: '0.5rem',
gridTemplateColumns: isMobile ? 'repeat(auto-fit, minmax(180px, 1fr))' : '1fr',
}}
>
{navItems.map((item, index) => {
if (item.type === 'separator') {
return (
<div
key={`separator-${index}`}
style={{
height: 1,
background: 'rgba(255, 255, 255, 0.16)',
margin: '0.35rem 0',
}}
/>
);
}
const isActive = activeNavItem ? item.id === activeNavItem : index === 0;
return (
<button
key={item.id}
type="button"
onClick={() => {
if (item.path) {
navigate(item.path);
return;
}
onNavItemChange?.(item.id);
}}
style={{
border: 'none',
borderRadius: '18px',
padding: '0.9rem 1rem',
background: isActive ? 'rgba(255, 255, 255, 0.14)' : 'transparent',
color: '#fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
fontWeight: isActive ? 700 : 500,
width: '100%',
textAlign: 'left',
}}
>
<span>{item.label}</span>
{item.count ? (
<span
style={{
minWidth: 30,
borderRadius: 999,
padding: '0.2rem 0.5rem',
background: 'rgba(255, 255, 255, 0.12)',
fontSize: '0.82rem',
textAlign: 'center',
}}
>
{item.count}
</span>
) : null}
</button>
);
})}
</nav>
<button
type="button"
onClick={handleLogout}
style={{
border: '1px solid rgba(255, 255, 255, 0.18)',
borderRadius: '18px',
padding: '0.9rem 1rem',
background: 'transparent',
color: '#fff',
fontWeight: 700,
textAlign: 'left',
}}
>
Sair
</button>
</aside>
</div>
<div style={{ display: 'grid', gap: '1.25rem', minWidth: 0 }}>
<header
style={{
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : 'minmax(0, 1fr) auto',
gap: '1rem',
alignItems: 'center',
}}
>
<div
style={{
padding: '1.1rem 1.25rem',
borderRadius: '22px',
background: '#fff',
border: '1px solid var(--color-border)',
minWidth: 0,
}}
>
<h1 style={{ margin: 0, fontSize: '1.65rem' }}>{title}</h1>
<p style={{ margin: '0.45rem 0 0', color: 'var(--color-text-soft)' }}>
{subtitle}
</p>
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '0.9rem',
justifySelf: isMobile ? 'stretch' : 'end',
justifyContent: isMobile ? 'space-between' : 'flex-end',
padding: '0.85rem 1rem',
borderRadius: '22px',
background: '#fff',
border: '1px solid var(--color-border)',
}}
>
<div style={{ textAlign: 'right' }}>
<strong style={{ display: 'block' }}>{profileLabel}</strong>
<span style={{ color: 'var(--color-text-soft)', fontSize: '0.92rem' }}>
Ambiente de gestão
</span>
</div>
<div
aria-hidden="true"
style={{
width: 48,
height: 48,
borderRadius: '16px',
display: 'grid',
placeItems: 'center',
background: 'linear-gradient(135deg, var(--color-accent), var(--color-primary))',
color: '#fff',
fontWeight: 800,
}}
>
{initials}
</div>
</div>
</header>
{children}
</div>
</div>
</section>
</main>
);
}