"use client"; import { useState, useRef } from "react"; import Vapi from "@vapi-ai/web"; const VAPI_PUBLIC_KEY = "d44a0025-24bb-426d-919a-cb0a96416ed4"; const ASSISTANT_ID = "92630ca5-e165-4360-bce0-dd8730882569"; interface SiteMenteVoiceWidgetProps { businessName?: string; businessType?: "restaurant" | "real-estate" | "clinic" | "car-rental" | "default"; theme?: "dark" | "light"; } export default function SiteMenteVoiceWidget({ businessName = "SiteMente", businessType = "default", theme = "dark" }: SiteMenteVoiceWidgetProps) { const [isActive, setIsActive] = useState(false); const [status, setStatus] = useState<"idle" | "connecting" | "active" | "error">("idle"); const [transcript, setTranscript] = useState(""); const [errorMsg, setErrorMsg] = useState(""); const vapiRef = useRef(null); const startCall = async () => { try { console.log("Starting call - initializing Vapi inside click handler..."); setErrorMsg(""); setStatus("connecting"); // Step 1: Verify mic exists try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); console.log("✅ Mic stream created:", stream); console.log("✅ Audio tracks:", stream.getAudioTracks().length); console.log("✅ Track enabled:", stream.getAudioTracks()[0]?.enabled); console.log("✅ Track settings:", stream.getAudioTracks()[0]?.getSettings()); } catch (micErr) { console.log("❌ Mic error:", micErr); } // Initialize Vapi INSIDE the click handler (required for iOS) const vapi = new Vapi(VAPI_PUBLIC_KEY); vapiRef.current = vapi; // Set up event listeners vapi.on("error", (error: any) => { console.log("Vapi error:", error); const msg = String(error?.message || error?.error?.message || JSON.stringify(error) || "Error desconocido"); setErrorMsg(msg); setStatus("error"); setIsActive(false); }); vapi.on("call-start", () => { console.log("✅ Call started!"); setStatus("active"); // Check peer connection for audio senders setTimeout(() => { try { // @ts-ignore - internal property const pc = vapiRef.current?._call?._pc; if (pc) { console.log("📡 PeerConnection found"); pc.getSenders().forEach((sender: any, i: number) => { console.log(`Sender ${i}:`, sender.track?.kind, sender.track?.enabled); }); } else { console.log("⚠️ No PeerConnection found"); } } catch (e) { console.log("Error checking PC:", e); } }, 2000); }); vapi.on("call-end", (e: any) => { console.log("Call ended", e); setStatus("idle"); setIsActive(false); }); vapi.on("message", (m: any) => { console.log("Vapi message:", m); }); vapi.on("speech-start", () => { console.log("User speech detected!"); }); vapi.on("speech-end", () => { console.log("User speech ended"); }); vapi.on("transcript", (transcript: any) => { console.log("Transcript:", transcript); if (typeof transcript === "string") { setTranscript(transcript); } else if (transcript?.text) { setTranscript(transcript.text); } }); console.log("Calling assistant:", ASSISTANT_ID); // Start the call await vapi.start(ASSISTANT_ID); console.log("Call started successfully"); setIsActive(true); } catch (error: any) { console.log("Start error:", error); const msg = String(error?.message || error?.error?.message || JSON.stringify(error) || "Error al iniciar"); setErrorMsg(msg); setStatus("error"); } }; const endCall = async () => { try { if (vapiRef.current) { await vapiRef.current.stop(); } setIsActive(false); setStatus("idle"); setTranscript(""); } catch (error) { console.error("End call error:", error); } }; const buttonColor = theme === "dark" ? "bg-brand-pink" : "bg-blue-600"; return (
{status === "error" && errorMsg && (
⚠️ {errorMsg}
)} {isActive && (
🤖 AI
{transcript || "Escuchando..."}
)} {status === "connecting" && (
Conectando...
)}
); }