/* ===== Growing Now — Agent Chat System (Groq) ===== */
(function () {
const { useState, useEffect, useRef } = React;
const Icon = window.Icon;
const PRIVACY_NOTE = {
es: 'No compartas contraseñas ni información sensible. La IA puede equivocarse.',
en: 'Do not share passwords or sensitive information. AI can make mistakes.'
};
const GREETINGS = {
zuki: { es: 'Hola, soy Zuki 🦊, tu aliada para organizar ideas y convertirlas en acciones.', en: "Hi, I'm Zuki 🦊, your ally for organizing ideas and turning them into actions." },
tula: { es: 'Hola, soy Tula 🐢, estoy aquí para ayudarte a avanzar con calma y claridad.', en: "Hi, I'm Tula 🐢, I'm here to help you move forward with calm and clarity." },
piplo: { es: 'Hola, soy Piplo 🐦, observo conversaciones para descubrir oportunidades de crecimiento.', en: "Hi, I'm Piplo 🐦, I observe conversations to discover growth opportunities." },
nox: { es: 'Hola, soy Nox 🐺, tu estratega para detectar oportunidades y tomar mejores decisiones.', en: "Hi, I'm Nox 🐺, your strategist for spotting opportunities and making better decisions." },
hugo: { es: 'Hola, soy Hugo 🦉, tu creativo para convertir ideas en mensajes que conectan.', en: "Hi, I'm Hugo 🦉, your creative for turning ideas into messages that connect." },
max: { es: 'Hola, soy Max 🐻, tu aliado para transformar planes en resultados.', en: "Hi, I'm Max 🐻, your ally for transforming plans into results." }
};
async function callGroq(agentId, messages, lang) {
const r = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agentId,
lang,
messages
})
});
if (!r.ok) throw new Error('API error');
const d = await r.json();
return d.text;
}
/* ── AgentChatPanel: panel modal para cualquier agente ── */
function AgentChatPanel({ agent, lang, onClose }) {
const id = agent.id;
const greeting = GREETINGS[id]?.[lang] || GREETINGS.zuki[lang];
const [msgs, setMsgs] = useState([{ role: 'assistant', text: greeting }]);
const [input, setInput] = useState('');
const [busy, setBusy] = useState(false);
const bodyRef = useRef(null);
useEffect(() => {
setMsgs([{ role: 'assistant', text: GREETINGS[id]?.[lang] || GREETINGS.zuki[lang] }]);
}, [id, lang]);
useEffect(() => {
if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
}, [msgs, busy]);
async function send(text) {
const content = (text ?? input).trim();
if (!content || busy) return;
const history = [...msgs, { role: 'user', text: content }];
setMsgs(history); setInput(''); setBusy(true);
const apiMsgs = history.map(m => ({ role: m.role === 'user' ? 'user' : 'assistant', content: m.text }));
try {
const reply = await callGroq(id, apiMsgs, lang);
setMsgs(m => [...m, { role: 'assistant', text: reply }]);
} catch {
setMsgs(m => [...m, { role: 'assistant', text: lang === 'es'
? `Ahora mismo no puedo conectar 😅 ¡Agenda una llamada con el botón de arriba!`
: `Can't connect right now 😅 Book a call with the button above!` }]);
}
setBusy(false);
}
return (
e.stopPropagation()} className="glass modal-card"
style={{ maxWidth: 420, width: '100%', height: 'min(600px,calc(100vh - 80px))', borderRadius: 26,
display: 'flex', flexDirection: 'column', overflow: 'hidden',
background: `linear-gradient(180deg,${agent.tint}f0,rgba(255,255,255,.97))` }}>
{/* Header */}
{agent.name}
{agent.role[lang]}
{/* Messages */}
{msgs.map((m, i) => (
))}
{busy && (
{[0,1,2].map(i => )}
)}
{/* Cal.com */}
{lang === 'es' ? `Agendar llamada con ${agent.name}` : `Book a call with ${agent.name}`}
{/* Input */}
setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder={lang === 'es' ? `Escríbele a ${agent.name}…` : `Message ${agent.name}…`}
style={{ flex: 1, border: 'none', outline: 'none', background: 'rgba(0,0,0,.04)', borderRadius: 99, padding: '11px 16px', fontFamily: 'var(--font-body)', fontSize: '.95rem' }} />
{PRIVACY_NOTE[lang]}
);
}
/* ── ZukiLauncher: botón flotante + panel de Zuki ── */
function ZukiLauncher({ lang, openSignal }) {
const L = window.I18N.STR[lang];
const zuki = window.GN.byId.zuki;
const [open, setOpen] = useState(false);
const [msgs, setMsgs] = useState([{ role: 'assistant', text: GREETINGS.zuki[lang] }]);
const [input, setInput] = useState('');
const [busy, setBusy] = useState(false);
const bodyRef = useRef(null);
useEffect(() => { setMsgs([{ role: 'assistant', text: GREETINGS.zuki[lang] }]); }, [lang]);
useEffect(() => { if (openSignal) setOpen(true); }, [openSignal]);
useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [msgs, busy, open]);
async function send(text) {
const content = (text ?? input).trim();
if (!content || busy) return;
const history = [...msgs, { role: 'user', text: content }];
setMsgs(history); setInput(''); setBusy(true);
const apiMsgs = history.map(m => ({ role: m.role === 'user' ? 'user' : 'assistant', content: m.text }));
try {
const reply = await callGroq('zuki', apiMsgs, lang);
setMsgs(m => [...m, { role: 'assistant', text: reply }]);
} catch {
setMsgs(m => [...m, { role: 'assistant', text: lang === 'es'
? 'Ahora mismo no puedo conectar 😅. ¡Agenda una llamada con el botón de arriba!'
: "Can't reach my brain right now 😅. Book a call with the button above!" }]);
}
setBusy(false);
}
return (
<>
{/* Launcher button */}
{/* Zuki panel */}
{open && (
{/* Header */}
{L.chat_title}
{L.chat_role}
{/* Messages */}
{msgs.map((m, i) => (
))}
{busy && (
{[0,1,2].map(i => )}
)}
{/* Cal.com */}
{L.chat_book}
{/* Input */}
setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder={L.chat_ph}
style={{ flex: 1, border: 'none', outline: 'none', background: 'rgba(0,0,0,.04)', borderRadius: 99, padding: '11px 16px', fontFamily: 'var(--font-body)', fontSize: '.95rem' }} />
{PRIVACY_NOTE[lang]}
)}
>
);
}
window.AgentChatPanel = AgentChatPanel;
window.ZukiLauncher = ZukiLauncher;
})();