// UIComponents.jsx — Shared UI primitives 'use strict'; const { useState, useEffect, useRef } = React; // ── Toast ────────────────────────────────────────────────────────────────── function Toast({ toast, onRemove }) { useEffect(() => { const t = setTimeout(() => onRemove(toast.id), 3800); return () => clearTimeout(t); }, []); const cfg = { success: { bg:'#ECFDF5', border:'#10B981', icon:'CheckCircle', text:'#065F46' }, error: { bg:'#FEF2F2', border:'#EF4444', icon:'XCircle', text:'#991B1B' }, info: { bg:'#EFF6FF', border:'#3B82F6', icon:'Info', text:'#1E40AF' }, warning: { bg:'#FFFBEB', border:'#F59E0B', icon:'AlertCircle', text:'#92400E' }, }[toast.type || 'success']; return (
{toast.title &&

{toast.title}

}

{toast.msg}

); } function ToastContainer({ toasts, removeToast }) { return (
{toasts.map(t => )}
); } // ── Modal ────────────────────────────────────────────────────────────────── // Renderizado via portal no document.body para escapar de ancestrais com `transform` // (que criam novo containing block e quebram `position: fixed`). function Modal({ open, onClose, title, children, width = 'max-w-2xl', noPad = false }) { useEffect(() => { if (open) document.body.style.overflow = 'hidden'; else document.body.style.overflow = ''; return () => { document.body.style.overflow = ''; }; }, [open]); if (!open) return null; return ReactDOM.createPortal(
e.target === e.currentTarget && onClose()}>

{title}

{children}
, document.body ); } // ── ConfirmDialog ────────────────────────────────────────────────────────── function ConfirmDialog({ open, onClose, onConfirm, title = 'Confirmar exclusão', message, confirmLabel = 'Excluir', danger = true }) { if (!open) return null; return ReactDOM.createPortal(

{title}

{message}

, document.body ); } // ── StatusBadge ──────────────────────────────────────────────────────────── const STATUS_CFG = { 'Aberta': { bg:'#DBEAFE', color:'#1D4ED8', dot:'#3B82F6' }, 'Em andamento': { bg:'#FEF3C7', color:'#B45309', dot:'#F59E0B' }, 'Aguardando peça':{ bg:'#FCE7F3', color:'#9D174D', dot:'#EC4899' }, 'Concluída': { bg:'#D1FAE5', color:'#065F46', dot:'#10B981' }, 'Entregue': { bg:'#E0E7FF', color:'#3730A3', dot:'#6366F1' }, 'pago': { bg:'#D1FAE5', color:'#065F46', dot:'#10B981' }, 'pendente': { bg:'#FEF3C7', color:'#B45309', dot:'#F59E0B' }, 'entrada': { bg:'#D1FAE5', color:'#065F46', dot:'#10B981' }, 'saída': { bg:'#FEE2E2', color:'#991B1B', dot:'#EF4444' }, 'Ativo': { bg:'#D1FAE5', color:'#065F46', dot:'#10B981' }, 'Inativo': { bg:'#F1F5F9', color:'#64748B', dot:'#94A3B8' }, }; function StatusBadge({ status, size = 'sm' }) { const cfg = STATUS_CFG[status] || { bg:'#F1F5F9', color:'#64748B', dot:'#94A3B8' }; const px = size === 'xs' ? 'px-2 py-0.5 text-xs' : 'px-2.5 py-1 text-xs'; return ( {status} ); } // ── EmptyState ───────────────────────────────────────────────────────────── function EmptyState({ icon = 'FileText', title, message, actionLabel, onAction }) { return (

{title}

{message}

{actionLabel && onAction && ( )}
); } // ── Skeleton ─────────────────────────────────────────────────────────────── function Skeleton({ className = '', style = {} }) { return
; } function SkeletonRow() { return (
); } // ── Form Controls ────────────────────────────────────────────────────────── function FormField({ label, required, error, children, className = '' }) { return (
{label && } {children} {error &&

{error}

}
); } function Input({ className = '', error, ...props }) { return ( ); } function Select({ className = '', children, ...props }) { return ( ); } function Textarea({ className = '', ...props }) { return (