// 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) => PDF, Xlsx: (p) => XLS, Md: (p) => MD, 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 (
Análise prévia sem validade jurídica Sem validade jurídica · Documento interno e comercial · Geração assistida por IA — confirme cálculos antes de protocolar v1.0
); } // ─── 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 (
{children}
); } 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, });