"use client"; import React, { useRef, useState } from "react"; import { VoicePoweredOrb } from "@/components/ZoeIA/voice-powered-orb"; import { Button } from "@/components/ui/button"; import { Mic, MicOff } from "lucide-react"; // ⚠ Coloque aqui o webhook real do seu n8n const N8N_WEBHOOK_URL = "https://n8n.jonasbomfim.store/webhook/zoe2"; const AIVoiceFlow: React.FC = () => { const [isRecording, setIsRecording] = useState(false); const [isSending, setIsSending] = useState(false); const [voiceDetected, setVoiceDetected] = useState(false); const [status, setStatus] = useState(null); const [error, setError] = useState(null); const [replyAudioUrl, setReplyAudioUrl] = useState(null); // URL do áudio retornado const [replyAudio, setReplyAudio] = useState(null); // elemento de áudio reproduzido const mediaRecorderRef = useRef(null); const streamRef = useRef(null); const chunksRef = useRef([]); // 🚀 Inicia gravação const startRecording = async () => { try { setError(null); setStatus("Iniciando microfone..."); // Se estava reproduzindo áudio da IA → parar imediatamente if (replyAudio) { replyAudio.pause(); replyAudio.currentTime = 0; } setReplyAudio(null); setReplyAudioUrl(null); const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); streamRef.current = stream; const recorder = new MediaRecorder(stream); mediaRecorderRef.current = recorder; chunksRef.current = []; recorder.ondataavailable = (e) => { if (e.data.size > 0) chunksRef.current.push(e.data); }; recorder.onstop = async () => { setStatus("Processando áudio..."); const blob = new Blob(chunksRef.current, { type: "audio/webm" }); await sendToN8N(blob); chunksRef.current = []; }; recorder.start(); setIsRecording(true); setStatus("Gravando... fale algo."); } catch (err) { console.error(err); setError("Erro ao acessar microfone."); } }; // ⏹ Finaliza gravação const stopRecording = () => { try { setIsRecording(false); setStatus("Finalizando gravação..."); if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") { mediaRecorderRef.current.stop(); } if (streamRef.current) { streamRef.current.getTracks().forEach((t) => t.stop()); streamRef.current = null; } } catch (err) { console.error(err); setError("Erro ao parar gravação."); } }; // 📤 Envia áudio ao N8N e recebe o MP3 const sendToN8N = async (audioBlob: Blob) => { try { setIsSending(true); setStatus("Enviando áudio para IA..."); const formData = new FormData(); formData.append("audio", audioBlob, "voz.webm"); const resp = await fetch(N8N_WEBHOOK_URL, { method: "POST", body: formData, }); if (!resp.ok) { throw new Error("N8N retornou erro"); } const replyBlob = await resp.blob(); // gera url local const url = URL.createObjectURL(replyBlob); setReplyAudioUrl(url); const audio = new Audio(url); setReplyAudio(audio); setStatus("Reproduzindo resposta..."); audio.play().catch(() => {}); } catch (err) { console.error(err); setError("Erro ao enviar/receber áudio."); } finally { setIsSending(false); } }; const toggleRecording = () => { if (isRecording) stopRecording(); else startRecording(); }; return (
{/* ORB — agora com comportamento inteligente */}
{isRecording && ( {voiceDetected ? "Ouvindo…" : "Aguardando voz…"} )}
{/* 🟣 Botão de gravação */} {/* STATUS */} {status &&

{status}

} {error &&

{error}

} {/* PLAYER MANUAL DA RESPOSTA */} {replyAudioUrl && (
Última resposta da IA:
)}
); }; export default AIVoiceFlow;