import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { WEBHOOK_URL } from '../lib/webrtc' import VoiceSettings from './VoiceSettings' import { buildVoiceCustoms } from '../lib/voiceOptions' const CALL_META = { university: { label: 'University Call Agent', tagline: 'Admissions', accent: '#60d0f0', icon: '⬡', description: 'AI admissions counsellor Sneha will call the number and guide the prospective student through the admissions process.', agentName: 'Sneha', defaultBrand: 'NIMS University', }, realestate: { label: 'Real Estate Call Agent', tagline: 'Sales', accent: '#f0b060', icon: '⬢', description: 'AI sales executive Riya will call the number and qualify the property buyer through a natural conversation.', agentName: 'Riya', defaultBrand: 'Skyline Greens', }, coaching: { label: 'Coaching Institute Agent', tagline: 'Admissions', accent: '#b088f0', icon: '◆', description: 'AI counsellor Aarohi will call the lead and book a scholarship test, counselling, or campus visit through a natural conversation.', agentName: 'Aarohi', defaultBrand: 'Bansal Classes', }, medical: { label: 'Medical OPD Agent', tagline: 'Front Desk', accent: '#5fd0a0', icon: '✚', description: 'AI front-desk assistant Meera answers the OPD line, books appointments, and routes queries — with a built-in safety interrupt for emergencies.', agentName: 'Meera', defaultBrand: 'Sanjeevani Hospital', }, hotel: { label: 'Hotel Concierge Agent', tagline: 'Pre-arrival', accent: '#f0a0c0', icon: '❖', description: 'AI concierge Kabir calls a confirmed guest before check-in to confirm arrival, capture preferences, and offer relevant add-ons.', agentName: 'Kabir', defaultBrand: 'The Grand Rambagh', }, } // Server URL — same host as WEBHOOK_URL, just port 7777 const SERVER_URL = WEBHOOK_URL export default function CallSetup({ type }) { const navigate = useNavigate() const meta = CALL_META[type] const [digits, setDigits] = useState('') const [brand, setBrand] = useState('') const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [voiceSettings, setVoiceSettings] = useState({ sttProvider: 'soniox', sttLanguages: ['hi', 'en'], }) if (!meta) { navigate('/', { replace: true }) return null } function handleDigits(e) { const val = e.target.value.replace(/\D/g, '').slice(0, 10) setDigits(val) setError('') } async function handleCall() { if (digits.length < 10) return setError('Enter a valid 10-digit number') setError('') setLoading(true) const sessionId = crypto.randomUUID() const phone = `+91${digits}` try { const res = await fetch(`${SERVER_URL}/call`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type, phone, brand: brand.trim() || undefined, session_id: sessionId, voiceSettings: Object.keys(voiceSettings).length ? buildVoiceCustoms(voiceSettings) : null, }), }) if (!res.ok) { const err = await res.json().catch(() => ({ error: 'Request failed' })) setError(err.error || 'Failed to place call') setLoading(false) return } navigate(`/call/${type}/${sessionId}`, { state: { phone, agentName: meta.agentName, label: meta.label }, }) } catch (e) { setError(e.message || 'Network error') setLoading(false) } } return (
{meta.icon}

{meta.label}

{(brand.trim() || meta.defaultBrand)} · {meta.tagline}

{meta.description}

setBrand(e.target.value)} maxLength={60} />
+91
e.key === 'Enter' && !loading && handleCall()} maxLength={10} autoFocus />
{error &&

{error}

}

The AI agent will call the number. You'll see the live conversation here.

) } const s = { page: { minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '48px 20px', position: 'relative', overflowY: 'auto', }, glow: { position: 'fixed', inset: 0, pointerEvents: 'none', }, inner: { width: '100%', maxWidth: 440, display: 'flex', flexDirection: 'column', position: 'relative', zIndex: 1, }, back: { alignSelf: 'flex-start', fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--muted)', background: 'none', border: 'none', cursor: 'pointer', letterSpacing: '.08em', padding: 0, marginBottom: 32, }, iconBox: { width: 56, height: 56, borderRadius: 14, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 24, marginBottom: 20, }, title: { fontSize: 'clamp(28px, 5vw, 42px)', fontWeight: 800, letterSpacing: '-.03em', lineHeight: 1.05, marginBottom: 6, }, tagline: { fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '.22em', textTransform: 'uppercase', marginBottom: 14, }, desc: { fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--muted)', lineHeight: 1.7, marginBottom: 36, }, fieldLabel: { fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '.18em', textTransform: 'uppercase', color: 'var(--muted)', marginBottom: 8, }, brandInput: { background: 'var(--surface)', border: '1px solid var(--border2)', borderRadius: 8, color: 'var(--text)', fontFamily: 'var(--mono)', fontSize: 15, padding: '14px 16px', outline: 'none', marginBottom: 20, }, inputGroup: { display: 'flex', alignItems: 'center', border: '1px solid var(--border2)', borderRadius: 8, overflow: 'hidden', marginBottom: 12, }, prefix: { fontFamily: 'var(--mono)', fontSize: 14, color: 'var(--muted)', padding: '14px 14px', background: 'var(--surface)', borderRight: '1px solid var(--border2)', flexShrink: 0, letterSpacing: '.06em', }, input: { flex: 1, background: 'var(--surface)', border: 'none', color: 'var(--text)', fontFamily: 'var(--mono)', fontSize: 18, padding: '14px 16px', outline: 'none', letterSpacing: '.1em', }, error: { fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--danger)', marginBottom: 12, letterSpacing: '.06em', }, callBtn: { color: '#0a0a0a', border: 'none', fontFamily: 'var(--sans)', fontSize: 14, fontWeight: 700, letterSpacing: '.06em', padding: '16px 24px', borderRadius: 8, cursor: 'pointer', marginTop: 8, marginBottom: 16, transition: 'opacity .15s', }, fine: { fontFamily: 'var(--mono)', fontSize: 10, color: 'var(--muted)', letterSpacing: '.1em', textAlign: 'center', lineHeight: 1.6, }, }