David Prince
Add KYC, PIN, airtime, data, purity memory
d077e09
Raw
History Blame Contribute Delete
4.91 kB
import { useState, useRef } from "react";
import API from "../api";
import "./PurityAI.css";
const ELEVEN_KEY = "sk_2d090a3d6c13a30f2a89c5913a30c3f83ac82ff99a46c9b8";
const VOICE_ID = "EXAVITQu4vr4xnSDxMaL";
export default function PurityAI({ balance, onTransfer, onExternalTransfer }) {
const [active, setActive] = useState(false);
const [status, setStatus] = useState("idle");
const [transcript, setTranscript] = useState("");
const [response, setResponse] = useState("");
const speak = async (text) => {
setStatus("speaking");
setResponse(text);
try {
const res = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`, {
method: "POST",
headers: {
"xi-api-key": ELEVEN_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
text,
model_id: "eleven_monolingual_v1",
voice_settings: { stability: 0.5, similarity_boost: 0.75 },
}),
});
if (!res.ok) throw new Error("ElevenLabs failed");
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const audio = new Audio(url);
audio.onended = () => setStatus("idle");
await audio.play();
} catch {
const u = new SpeechSynthesisUtterance(text);
u.rate = 0.9; u.pitch = 1.1;
const voices = speechSynthesis.getVoices();
const female = voices.find(v => v.name.includes("Female") || v.name.includes("Samantha") || v.name.includes("Google UK English Female"));
if (female) u.voice = female;
u.onend = () => setStatus("idle");
speechSynthesis.speak(u);
}
};
const handleCommand = async (text) => {
const lower = text.toLowerCase();
if (lower.includes("balance") || lower.includes("how much")) {
const bal = balance?.balance?.toLocaleString("en-NG") ?? "unavailable";
speak(`Your available balance is ${bal} Naira.`);
} else if (lower.includes("send") || lower.includes("transfer") || lower.includes("pay")) {
speak("Opening the transfer screen for you right now.");
setTimeout(() => onTransfer && onTransfer(), 1500);
} else if (lower.includes("bank") || lower.includes("external") || lower.includes("moniepoint")) {
speak("Opening external bank transfer.");
setTimeout(() => onExternalTransfer && onExternalTransfer(), 1500);
} else if (lower.includes("hello") || lower.includes("hi") || lower.includes("hey")) {
speak(`Hello! I'm Purity, your dolor3v assistant. I can check your balance, help you send money, or transfer to other banks. What would you like to do?`);
} else if (lower.includes("account") || lower.includes("number")) {
speak(`Your account number is ${balance?.account_number ?? "unavailable"}.`);
} else {
speak("I can help you check your balance, send money, or make a bank transfer. Just tell me what you need!");
}
};
const startListening = () => {
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SR) {
speak("Voice recognition isn't available on this browser. Try Chrome.");
return;
}
const r = new SR();
r.lang = "en-US";
r.onstart = () => setStatus("listening");
r.onresult = (e) => {
const text = e.results[0][0].transcript;
setTranscript(text);
handleCommand(text);
};
r.onerror = () => { setStatus("idle"); speak("I didn't catch that. Please try again."); };
r.onend = () => { if (status === "listening") setStatus("idle"); };
r.start();
};
const handleTap = () => {
if (!active) {
setActive(true);
speak("Hi! I'm Purity, your dolor3v banking assistant. How can I help you today?");
} else {
startListening();
}
};
return (
<div className="purity-wrap">
<div className={`purity-orb ${status}`} onClick={handleTap}>
<div className="orb-ring" />
<div className="orb-ring r2" />
<div className="orb-core">
{status === "listening" ? "🎙" : status === "speaking" ? "🔊" : "✦"}
</div>
</div>
<h2 className="purity-name">Purity AI</h2>
<p className="purity-sub">
{status === "listening" ? "Listening..." : status === "speaking" ? "Speaking..." : active ? "Tap to speak" : "Tap to wake up"}
</p>
{transcript && (
<div className="purity-transcript">You: "{transcript}"</div>
)}
{response && (
<div className="purity-response">Purity: "{response}"</div>
)}
<div className="purity-hints">
<span onClick={() => handleCommand("check my balance")}>💰 Balance</span>
<span onClick={() => handleCommand("send money")}>↑ Send</span>
<span onClick={() => handleCommand("transfer to bank")}>🏦 Bank</span>
<span onClick={() => handleCommand("hello")}>👋 Hi</span>
</div>
</div>
);
}