// feedback-vendedora.jsx β€” FAB + modal de feedback para beta-testers // Monta un botΓ³n flotante en TODAS las pantallas de la vendedora. // Submit: POST directo a Supabase tabla beta_feedback. (function () { const SUPABASE_TABLE = 'beta_feedback'; const MAX_CHARS = 1000; const TIPOS = [ { value: 'bug', label: 'πŸ› Bug', placeholder: 'DescribΓ­ quΓ© pasΓ³ y en quΓ© pantalla ocurriΓ³...' }, { value: 'sugerencia', label: 'πŸ’‘ Sugerencia', placeholder: 'Contame quΓ© mejorarΓ­as o quΓ© te gustarΓ­a tener...' }, { value: 'duda', label: '❓ Duda', placeholder: 'ΒΏQuΓ© no te quedΓ³ claro o no sabΓ©s cΓ³mo hacer?' }, { value: 'elogio', label: '❀️ Elogio', placeholder: 'ΒΏQuΓ© te gustΓ³? Tu feedback positivo nos ayuda a saber quΓ© mantener.' }, ]; const SEVERIDADES = [ { value: 'baja', label: 'Baja' }, { value: 'media', label: 'Media' }, { value: 'alta', label: 'Alta' }, { value: 'critica', label: 'CrΓ­tica' }, ]; // ── helpers ─────────────────────────────────────────────────────────────── function getVendedoraAuth() { // 1. Preferimos el objeto completo cacheado por App try { const raw = localStorage.getItem('inova-vend'); if (raw) { const v = JSON.parse(raw); if (v && v.id) return v; } } catch {} return null; } // ── FeedbackModal ───────────────────────────────────────────────────────── function FeedbackModal({ screen, vendedora, onClose }) { const T = window.T; const [tipo, setTipo] = React.useState('bug'); const [severidad, setSeveridad] = React.useState('media'); const [mensaje, setMensaje] = React.useState(''); const [estado, setEstado] = React.useState('idle'); // idle | sending | ok | err | err404 const overlayRef = React.useRef(null); // Focus trap: enfocar el primer input al abrir const firstFocusRef = React.useRef(null); React.useEffect(() => { if (firstFocusRef.current) firstFocusRef.current.focus(); }, []); // Cerrar con ESC React.useEffect(() => { const handler = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [onClose]); // Bloquear scroll del body mientras estΓ‘ abierto React.useEffect(() => { const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = prev; }; }, []); const currentPlaceholder = TIPOS.find(t => t.value === tipo)?.placeholder || ''; const charsLeft = MAX_CHARS - mensaje.length; const canSubmit = mensaje.trim().length > 0 && estado === 'idle'; const handleSubmit = async () => { if (!canSubmit) return; setEstado('sending'); const vend = vendedora || getVendedoraAuth(); const payload = { vendedora_id: vend?.id || null, vendedora_nombre: vend?.nombre || null, vendedora_cedula: vend?.cedula || null, tipo, severidad: tipo === 'bug' ? severidad : 'baja', mensaje: mensaje.trim().slice(0, MAX_CHARS), pantalla: screen || 'desconocida', url: window.location.pathname + (window.location.hash || ''), user_agent: navigator.userAgent, }; try { const sb = await window.getSupabase(); const { error, status } = await sb .from(SUPABASE_TABLE) .insert([payload]); if (error) { if (status === 404 || (error.code && error.code === '42P01')) { setEstado('err404'); } else { setEstado('err'); } } else { setEstado('ok'); // Cierra el modal a los 2.5 s para que lean el toast setTimeout(() => onClose(), 2500); } } catch (e) { // 404 puede venir tambiΓ©n como network error con status 0 cuando la tabla no existe if (e?.status === 404 || String(e).includes('does not exist') || String(e?.message).includes('does not exist')) { setEstado('err404'); } else { setEstado('err'); } } }; // ── Overlay click fuera β†’ cerrar ─────────────────────────────────────── const handleOverlayClick = (e) => { if (e.target === overlayRef.current) onClose(); }; return (
{/* Drawer panel */}
e.stopPropagation()} > {/* Handle bar */}
{/* Header */}
Enviar feedback
{screen && (
pantalla: {screen}
)}
{/* Success toast */} {estado === 'ok' && (
Β‘Gracias! Ya leemos tu feedback πŸ’Œ
)} {/* Error: tabla no existe */} {estado === 'err404' && (
Estamos preparando el sistema de feedback, intenta en unos minutos.
)} {/* Error: otro */} {estado === 'err' && (
Algo fallΓ³, intentΓ‘ de nuevo o avisame por WhatsApp.
)} {estado !== 'ok' && ( <> {/* Tipo */}
Tipo
{TIPOS.map(t => ( ))}
{/* Severidad (solo si tipo=bug) */} {tipo === 'bug' && (
Severidad
{SEVERIDADES.map(s => ( ))}
)} {/* Mensaje */}
Mensaje