// Shared UI bits: Logo, icons, Badge, Spinner, Footer, Sidebar, Modal shell, etc.
const { useEffect, useState, useMemo, useRef, useCallback } = React;
// ─── Logo ──────────────────────────────────────────────────────────────────────
// Ícone: FontAwesome magnifying-glass-chart, amarelo #FFD43B (mesmo do favicon).
function Logo({ size = 'md', mono = false }) {
const dims = { sm: 20, md: 24, lg: 30, xl: 42 }[size] || 24;
const text = { sm: 'text-base', md: 'text-lg', lg: 'text-2xl', xl: 'text-4xl' }[size] || 'text-lg';
const color = mono ? '#1a1a1a' : '#FFD43B';
return (
Periciei
);
}
// ─── Icons (minimal inline svg) ────────────────────────────────────────────────
const Icon = {
Plus: (p) => ,
Search: (p) => ,
Home: (p) => ,
Doc: (p) => ,
Coins: (p) => ,
Cog: (p) => ,
Download:(p) => ,
Logout: (p) => ,
Menu: (p) => ,
X: (p) => ,
ArrowR: (p) => ,
Upload: (p) => ,
Check: (p) => ,
Alert: (p) => ,
Info: (p) => ,
Pdf: (p) => ,
Xlsx: (p) => ,
Md: (p) => ,
Copy: (p) => ,
};
// ─── Spinner ───────────────────────────────────────────────────────────────────
function Spinner({ size = 14, color = '#1e3a8a' }) {
return (
);
}
// ─── Badge ─────────────────────────────────────────────────────────────────────
const STATUS_LABEL = {
QUEUED: 'Em fila',
EXTRACTING: 'Extraindo PDF',
COMPUTING: 'Calculando',
DRAFTING: 'Redigindo',
RENDERING: 'Renderizando',
COMPLETE: 'Concluída',
FAILED: 'Falhou',
};
const INPROGRESS = new Set(['EXTRACTING', 'COMPUTING', 'DRAFTING', 'RENDERING']);
function StatusBadge({ status, size = 'sm' }) {
const sz = size === 'lg' ? 'px-3 py-1 text-[12.5px]' : 'px-2 py-0.5 text-[11.5px]';
let cls, dot, pulse = false;
if (status === 'COMPLETE') { cls = 'bg-ok-50 text-ok-700 border-ok-200'; dot = 'bg-ok-600'; }
else if (status === 'FAILED') { cls = 'bg-danger-50 text-danger-700 border-danger-200'; dot = 'bg-danger-600'; }
else if (status === 'QUEUED') { cls = 'bg-warn-50 text-warn-700 border-warn-200'; dot = 'bg-warn-600'; }
else if (INPROGRESS.has(status)) { cls = 'bg-navy-50 text-navy-800 border-navy-200'; dot = 'bg-navy-800'; pulse = true; }
else { cls = 'bg-ink-100 text-ink-700 border-ink-200'; dot = 'bg-ink-400'; }
return (
{STATUS_LABEL[status] || status}
);
}
function ViabilityChip({ value, size = 'md' }) {
if (!value) return —;
const sim = value === 'SIM';
const sz = size === 'lg'
? 'px-3.5 py-1.5 text-[13px]'
: 'px-2.5 py-0.5 text-[11.5px]';
return sim ? (
VIÁVEL
) : (
INVIÁVEL
);
}
function TenantStatusBadge({ status }) {
const map = {
ACTIVE: { cls: 'bg-ok-50 text-ok-700 border-ok-200', label: 'Ativo' },
TRIAL: { cls: 'bg-warn-50 text-warn-700 border-warn-200', label: 'Trial' },
SUSPENDED: { cls: 'bg-danger-50 text-danger-700 border-danger-200', label: 'Suspenso' },
CANCELLED: { cls: 'bg-ink-100 text-ink-700 border-ink-200', label: 'Cancelado' },
};
const m = map[status] || map.ACTIVE;
return {m.label};
}
// ─── Buttons ──────────────────────────────────────────────────────────────────
function Button({ variant = 'primary', size = 'md', className = '', children, ...rest }) {
const base = 'inline-flex items-center justify-center gap-2 font-medium rounded-md transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed select-none';
const sizes = {
sm: 'h-8 px-3 text-[13px]',
md: 'h-9 px-4 text-[13.5px]',
lg: 'h-11 px-5 text-[14.5px]',
};
const variants = {
primary: 'bg-navy-800 text-white hover:bg-navy-900',
secondary: 'bg-white text-ink-900 border border-ink-200 hover:border-ink-300 hover:bg-ink-50',
ghost: 'text-ink-700 hover:bg-ink-100',
danger: 'bg-white text-danger-700 border border-danger-200 hover:bg-danger-50',
outline: 'bg-white text-navy-800 border border-navy-800/60 hover:bg-navy-50',
};
return (
);
}
// ─── Inputs ────────────────────────────────────────────────────────────────────
function Field({ label, hint, error, children, suffix }) {
return (
);
}
function TextInput({ className = '', ...rest }) {
return (
);
}
function Select({ className = '', children, ...rest }) {
return (
);
}
// ─── Sidebar / Topbar ──────────────────────────────────────────────────────────
function Sidebar({ current, onNavigate, tenant, user, usage, onLogout, isOpen, onClose }) {
const isSuperadmin = user && user.role === 'SUPERADMIN';
// SUPERADMIN acumula os dois chapéus: vê o admin de plataforma E o escritório dele.
const items = isSuperadmin
? [
{ id: 'admin', label: 'Painel admin', icon: , group: 'PLATAFORMA' },
{ id: 'dashboard', label: 'Dashboard', icon: , group: 'ESCRITÓRIO' },
{ id: 'analises', label: 'Análises', icon: },
{ id: 'creditos', label: 'Créditos', icon: },
{ id: 'settings', label: 'Configurações', icon: },
]
: [
{ id: 'dashboard', label: 'Dashboard', icon: },
{ id: 'analises', label: 'Análises', icon: },
{ id: 'creditos', label: 'Créditos', icon: },
{ id: 'settings', label: 'Configurações', icon: },
];
return (
<>
{/* Backdrop só no mobile, quando o drawer está aberto */}
{isOpen && (
)}
>
);
}
function Topbar({ title, breadcrumb, right, onMenuClick }) {
return (
{/* Burger só no mobile */}
{breadcrumb && (
{breadcrumb}
)}
{title &&
{title}
}
{right}
);
}
// ─── Footer legal ─────────────────────────────────────────────────────────────
function LegalFooter() {
return (
);
}
// ─── Modal shell ──────────────────────────────────────────────────────────────
function Modal({ open, onClose, children, width = 'max-w-xl', closeOnBack = true, label }) {
useEffect(() => {
if (!open) return;
const onKey = (e) => { if (e.key === 'Escape') onClose?.(); };
document.addEventListener('keydown', onKey);
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', onKey);
document.body.style.overflow = prev;
};
}, [open]);
if (!open) return null;
return (
);
}
function ModalHeader({ title, subtitle, onClose }) {
return (
{title}
{subtitle &&
{subtitle}
}
);
}
// ─── Stat tile ────────────────────────────────────────────────────────────────
function StatTile({ label, value, hint, accent }) {
return (
{label}
{value}
{hint &&
{hint}
}
);
}
// expose
Object.assign(window, {
Logo, Icon, Spinner, StatusBadge, ViabilityChip, TenantStatusBadge,
Button, Field, TextInput, Select,
Sidebar, Topbar, LegalFooter, Modal, ModalHeader, StatTile,
STATUS_LABEL, INPROGRESS,
});