// Modals — SOFIA Chat send, SOFIA Call launch, Schedule call, Submit to client, // Interview kit, Bulk campaign. LIGHT theme. const { useEffect: ueM, useState: usM } = React; // ---------- SOFIA Chat — send WhatsApp ---------- function SofiaChatModal({ open, onClose, candidate, defaultTemplate }) { const toast = useToast(); const [template, setTemplate] = usM(defaultTemplate?.id || 't1'); const [body, setBody] = usM(''); const [phase, setPhase] = usM('compose'); ueM(() => { if (!open) return; const t = DATA.TEMPLATES.find((x) => x.id === template); if (!candidate || !t) return; const text = t.body .replace('{{nombre}}', candidate.name.split(' ')[0]) .replace('{{rol}}', 'Asesor Bilingüe') .replace('{{cliente}}', 'Acme Retail') .replace('{{salario}}', 'USD $950') .replace('{{modalidad}}', 'Híbrido') .replace('{{fecha}}', 'mañana 10:00 am'); setBody(text); }, [open, template, candidate]); ueM(() => { if (open) setPhase('compose'); }, [open]); const send = () => { setPhase('sending'); setTimeout(() => { setPhase('sent'); toast?.push({ title: 'Mensaje enviado vía SOFIA Chat', body: `WhatsApp entregado a ${candidate?.name}.`, tone: 'success', }); setTimeout(() => onClose?.(), 950); }, 1100); }; if (!candidate) return null; return ( } title="Enviar mensaje vía SOFIA Chat" subtitle={`WhatsApp Business · destinatario: ${candidate.name}`} onClose={onClose} /> Plantilla {DATA.TEMPLATES.map((t) => ( setTemplate(t.id)} className={`w-full text-left p-3 rounded-lg border text-sm transition-colors ${ template === t.id ? 'border-brand-300 bg-brand-50/60 ring-2 ring-brand-100' : 'border-line bg-white hover:border-ink-200' }`} > {t.name} {t.body} ))} Vista previa {candidate.name} WhatsApp · en línea setBody(e.target.value)} rows={6} className="w-full bg-white/80 border border-line rounded-lg p-2 outline-none text-sm text-ink-900 resize-none focus:border-brand-400" /> {body} ahora Personalizado por SOFIA · variables auto-rellenadas Cancelar : } onClick={send} disabled={phase !== 'compose'} > {phase === 'compose' ? 'Enviar vía SOFIA Chat' : phase === 'sending' ? 'Enviando…' : 'Enviado'} ); } // ---------- SOFIA Call — launch ---------- function SofiaCallModal({ open, onClose, candidate }) { const toast = useToast(); const [phase, setPhase] = usM('idle'); const [seconds, setSeconds] = usM(0); const [waveform, setWaveform] = usM(Array.from({ length: 28 }, () => Math.random())); ueM(() => { if (open) { setPhase('idle'); setSeconds(0); } }, [open]); ueM(() => { if (phase !== 'live') return; const i = setInterval(() => { setSeconds((s) => s + 1); setWaveform(() => Array.from({ length: 28 }, () => Math.random())); }, 700); return () => clearInterval(i); }, [phase]); const dial = () => { setPhase('dialing'); setTimeout(() => setPhase('live'), 1400); }; const hangup = () => { setPhase('done'); toast?.push({ title: 'Screening guardado', body: `SOFIA Call generó transcripción y recomendación para ${candidate?.name}.`, tone: 'brand', }); setTimeout(() => onClose?.(), 1100); }; if (!candidate) return null; const mm = String(Math.floor(seconds / 60)).padStart(2, '0'); const ss = String(seconds % 60).padStart(2, '0'); return ( } title="SOFIA Call · screening AI" subtitle="Llamada saliente automatizada · español/inglés" onClose={onClose} /> {candidate.name} +503 7•••-2{candidate.id.slice(-2)}1 {candidate.city} }>SOFIA MCP {phase === 'idle' && 'Listo para marcar'} {phase === 'dialing' && 'Marcando…'} {phase === 'live' && 'En llamada con SOFIA'} {phase === 'done' && 'Llamada finalizada'} {mm}:{ss} {waveform.map((v, i) => ( ))} Script · 5 preguntas {DATA.SCREENING_QUESTIONS.map((q, i) => ( {i + 1}. {q} ))} Llamada grabada y con consentimiento {phase === 'idle' && ( <> Cancelar } onClick={dial}>Iniciar llamada > )} {phase === 'dialing' && ( } disabled>Conectando… )} {phase === 'live' && ( } onClick={hangup}>Finalizar )} {phase === 'done' && ( } onClick={onClose}>Listo )} ); } // ---------- Schedule call ---------- function ScheduleCallModal({ open, onClose, candidate }) { const toast = useToast(); const [day, setDay] = usM('Mañana'); const [time, setTime] = usM('10:00'); const days = ['Hoy', 'Mañana', 'Jue 15', 'Vie 16', 'Lun 19']; const times = ['09:00', '10:00', '11:00', '14:00', '15:30', '17:00']; const confirm = () => { toast?.push({ title: 'Screening agendado', body: `SOFIA Call llamará a ${candidate?.name} ${day} a las ${time}.`, tone: 'brand', }); onClose?.(); }; if (!candidate) return null; return ( } title="Agendar screening con SOFIA Call" subtitle={candidate.name} onClose={onClose} /> Día {days.map((d) => ( setDay(d)} className={`h-9 px-3 rounded-lg text-sm border transition-colors ${ day === d ? 'border-brand-400 bg-brand-50 text-brand-700' : 'bg-white border-line text-ink-700 hover:border-ink-200' }`}> {d} ))} Hora {times.map((t) => ( setTime(t)} className={`h-9 rounded-lg text-sm tabular border transition-colors ${ time === t ? 'border-brand-400 bg-brand-50 text-brand-700' : 'bg-white border-line text-ink-700 hover:border-ink-200' }`}> {t} ))} SOFIA reutilizará el script actual de 5 preguntas y subirá la transcripción al perfil al finalizar. Cancelar } onClick={confirm}>Confirmar ); } // ---------- Submit to client ---------- function SubmitToClientModal({ open, onClose, candidate, client = 'Acme Retail Group' }) { const toast = useToast(); const [note, setNote] = usM('Recomiendo fuertemente. Inglés C1 verificado por SOFIA Call. Disponibilidad inmediata.'); const submit = () => { toast?.push({ title: `${candidate?.name} submitido`, body: `Enviado a ${client} para revisión final.`, tone: 'success' }); onClose?.(); }; if (!candidate) return null; return ( } title={`Submitir candidato a ${client}`} subtitle="Se enviará paquete con CV, transcripción de screening y match analysis" onClose={onClose} /> {candidate.name} {candidate.role} · {candidate.yearsExperience} años Match {candidate.matchScore}% Nota para el cliente setNote(e.target.value)} rows={4} className="w-full bg-white border border-line focus:border-brand-400 outline-none rounded-lg p-3 text-sm text-ink-900 resize-none" /> {[ { icon: , label: 'CV completo' }, { icon: , label: 'Transcripción SOFIA Call' }, { icon: , label: 'Match analysis' }, ].map((x) => ( {x.icon} {x.label} ))} Cancelar } onClick={submit}>Submitir a {client} ); } // ---------- Interview kit ---------- function InterviewKitModal({ open, onClose, candidate }) { const toast = useToast(); if (!candidate) return null; const kit = [ { section: 'Apertura', items: ['Rompe-hielo personalizado: hobbies, idiomas, mudanza reciente', 'Resumen de SOFIA (60 seg) sobre fortalezas y gaps'] }, { section: 'Profundización técnica', items: ['Caso role-play: cliente moroso (15 min)', 'Validación de inglés C1: 3 preguntas espontáneas', 'Manejo de Salesforce: walkthrough en pantalla compartida'] }, { section: 'Cultural & motivación', items: ['¿Por qué Acme Retail vs. otra opción?', '¿Cómo describirías un equipo donde brillas?'] }, { section: 'Cierre', items: ['Compensación y modalidad', 'Próximos pasos: oferta en 48h si avanza'] }, ]; return ( } title="Kit de entrevista generado por SOFIA" subtitle={`Personalizado para ${candidate.name} · Acme Retail · Bilingüe`} onClose={onClose} /> {kit.map((s) => ( {s.section} {s.items.map((it, i) => ( {it} ))} ))} SOFIA recomienda Empieza por el caso role-play; el screening detectó muy buen manejo de objeciones pero quieres confirmar bajo presión real. Duración estimada 45 min + 10 min de feedback con el comité } onClick={() => { toast?.push({ title: 'Kit descargado', tone: 'success' }); }}> Descargar PDF ); } // ---------- Bulk WhatsApp campaign ---------- function BulkCampaignModal({ open, onClose }) { const toast = useToast(); const [segment, setSegment] = usM('Inglés B2+ · San Salvador · disponible inmediato'); const [template, setTemplate] = usM('t1'); const send = () => { toast?.push({ title: 'Campaña enviada', body: '47 mensajes vía SOFIA Chat encolados.', tone: 'brand' }); onClose?.(); }; return ( } title="Campaña masiva WhatsApp · SOFIA Chat" subtitle="Envía un mensaje personalizado a un segmento del Candidate Pool" onClose={onClose} /> Segmento } /> Plantilla base {DATA.TEMPLATES.slice(0, 4).map((t) => ( setTemplate(t.id)} className={`text-left p-3 rounded-lg border text-sm ${ template === t.id ? 'border-brand-300 bg-brand-50/60 ring-2 ring-brand-100' : 'border-line bg-white hover:border-ink-200' }`}> {t.name} {t.body} ))} Personalizar por candidato con SOFIA {}} /> Resumen Destinatarios 47 Costo SOFIA Chat USD $4.10 Open rate estimado 92% Mensajes/seg 12 Tiempo total ~4 seg } onClick={send}> Enviar campaña ); } Object.assign(window, { SofiaChatModal, SofiaCallModal, ScheduleCallModal, SubmitToClientModal, InterviewKitModal, BulkCampaignModal, });
Empieza por el caso role-play; el screening detectó muy buen manejo de objeciones pero quieres confirmar bajo presión real.