// Admin panel — wired to real API const { useState: aSt, useEffect: aEf, useRef: aRf } = React; // ───────── Admin Overview ───────── function AdminOverview() { const [overview, setOverview] = aSt(null); const [pulse, setPulse] = aSt({ summary: {}, recent: [] }); aEf(() => { let stop = false; (async () => { setOverview(await window.HDR_API.adminOverview().catch(() => null)); })(); const tick = async () => { if (stop) return; try { setPulse(await window.HDR_API.adminPulse()); } catch {} }; tick(); const id = setInterval(tick, 5000); return () => { stop = true; clearInterval(id); }; }, []); const u = overview?.users || {}; const us = overview?.usage || {}; const byEp = overview?.byEndpoint || []; return (

Admin Overview

Platform-wide KPIs and live request pulse. Auto-refreshes every 5s.

Total users} value={U.num(u.total || 0)} spark={[12,14,16,18,22,26,30]}/> Requests (30d)} value={U.num(us.reqs_30d || 0)} spark={[40,55,52,68,72,80,95]}/> Revenue (30d)} value={U.rupees(us.revenue_30d || 0, { short: true })} spark={[20,28,32,38,42,48,55]} sparkColor="var(--pos)"/> Failed (30d)} value={U.num(us.failed_30d || 0)} spark={[12,11,9,8,7,6,5]} sparkColor="var(--neg)"/>
Live activity
{pulse.summary?.reqs_60min || 0} req · {U.rupees(pulse.summary?.revenue_60min || 0)} · {pulse.summary?.active_users_60min || 0} active (last hour)
{(pulse.recent || []).length === 0 ? (
No recent activity
) : (pulse.recent || []).map(p => (
{p.status === 'success' ? 'OK' : 'FAIL'} {p.model_alias || '—'} {p.endpoint} {p.email || '—'} {U.rupees(p.cost_paise || 0)} {p.latency_ms || 0}ms
))}
Providers
{Object.entries(overview?.providers || {}).map(([name, ok]) => (
{name}
{ok ? 'configured' : 'missing'}
))}
Razorpay
Status
{overview?.razorpay?.configured ? (overview.razorpay.mode || 'test').toUpperCase() : 'Not configured'}
Endpoints (30d)
{byEp.length === 0 ? (
No data yet
) : byEp.map(e => (
{e.endpoint}
{e.c}
))}
); } // ───────── Analytics ───────── function AdminAnalytics() { const [days, setDays] = aSt(30); const { data, loading, reload } = useLoad(() => window.HDR_API.adminAnalytics(days), [days]); const series = (data?.series || []).map(s => ({ label: s.d?.slice(5) || '', v: Number(s.revenue) })); const totalRev = (data?.series || []).reduce((s, x) => s + Number(x.revenue), 0); const totalReq = (data?.series || []).reduce((s, x) => s + Number(x.reqs), 0); const totalFail = (data?.series || []).reduce((s, x) => s + Number(x.failed), 0); const successRate = totalReq ? (((totalReq - totalFail) / totalReq) * 100).toFixed(1) : '—'; return (

Analytics

Trends, top users, top models, provider performance.

{[7, 30, 90].map(d => ( ))}
Daily revenue
{loading ?
Loading…
: }
Top users by spend
{(data?.topUsers || []).map(u => ( ))} {(data?.topUsers || []).length === 0 && }
UserRequestsSpent
{u.email || '—'} {u.reqs} {U.rupees(u.spent)}
No data
Top model aliases
{(data?.topModels || []).map(m => ( ))} {(data?.topModels || []).length === 0 && }
AliasRequestsRevenue
{m.model_alias} {m.c} {U.rupees(m.revenue)}
No data
Provider performance
{(data?.providerHealth || []).map(p => { const rate = p.total ? ((p.ok / p.total) * 100).toFixed(1) : '—'; return ( ); })} {(data?.providerHealth || []).length === 0 && }
ProviderTotalSuccessFailedSuccess rateAvg latency
{p.provider} {p.total} {p.ok} {p.fail} = 95 ? 'pos' : 'warn')}>{rate}% {Math.round(p.avg_latency || 0)}ms
No data
); } // ───────── Users ───────── function AdminUsers() { const [q, setQ] = aSt(''); const [users, setUsers] = aSt([]); const [loading, setLoading] = aSt(true); const toast = useToast(); const timer = aRf(null); const load = async (qv) => { setLoading(true); try { setUsers(await window.HDR_API.adminUsers(qv || '')); } finally { setLoading(false); } }; aEf(() => { load(''); }, []); const onSearch = (v) => { setQ(v); if (timer.current) clearTimeout(timer.current); timer.current = setTimeout(() => load(v), 300); }; const credit = async (u) => { const inr = parseFloat(prompt(`Credit how much to ${u.email}? (₹)`)); if (!inr || inr <= 0) return; const note = prompt('Note:', 'Manual top-up') || 'Admin credit'; try { const r = await window.HDR_API.creditUser(u.id, { amount_inr: inr, note }); if (r.ok) { toast('Credited', 'pos'); load(q); } else throw new Error(r.error); } catch (e) { toast('Failed: ' + e.message, 'neg'); } }; const toggle = async (u) => { if (!confirm(u.is_active ? 'Suspend this user?' : 'Activate this user?')) return; await window.HDR_API.toggleUser(u.id); toast('Updated', 'pos'); load(q); }; const toggleAdmin = async (u) => { if (!confirm(u.is_admin ? 'Remove admin role?' : 'Make admin?')) return; const r = await window.HDR_API.toggleAdmin(u.id); if (r.error) toast('Failed: ' + r.error, 'neg'); else { toast('Updated', 'pos'); load(q); } }; return (

Users

Manage user accounts, wallet credits, status, and admin roles.

onSearch(e.target.value)} style={{maxWidth: 240}}/>
{loading && } {!loading && users.length === 0 && } {users.map(u => ( ))}
EmailNameWalletSpentJoinedStatusActions
Loading…
No users
{u.email} {!!u.is_admin && Admin} {u.name || '—'} {U.rupees(u.wallet_paise || 0)} {U.rupees(u.total_spent_paise || 0)} {new Date(u.created_at).toLocaleDateString('en-IN', { day:'2-digit', month:'short', year:'numeric' })} {u.is_active ? 'Active' : 'Suspended'}
); } // ───────── Models (routing chains) ───────── function AdminModels() { const { data: settings, reload } = useLoad(() => window.HDR_API.adminSettings(), []); const toast = useToast(); if (!settings) return
Loading…
; const types = [ { key: 'chat_routes', label: 'Chat', type: 'chat', data: settings.chat_routes }, { key: 'image_routes', label: 'Image', type: 'image', data: settings.image_routes }, { key: 'tts_routes', label: 'TTS', type: 'tts', data: settings.tts_routes }, { key: 'stt_routes', label: 'STT', type: 'stt', data: settings.stt_routes }, ]; return (

Models

Edit provider chains per alias. Saves instantly, no redeploy.

{types.map(t => )}
); } function ModelRoutesCard({ label, type, data, onSaved }) { const [json, setJson] = aSt(JSON.stringify(data || {}, null, 2)); const toast = useToast(); const save = async () => { try { const parsed = JSON.parse(json); const r = await window.HDR_API.saveRoutes(type, parsed); if (r.ok) { toast(`${label} routes saved`, 'pos'); onSaved && onSaved(); } else throw new Error(r.error); } catch (e) { toast('Invalid JSON: ' + e.message, 'neg'); } }; return (
{label} routing
{Object.keys(data || {}).length} aliases — edit JSON below