// User-facing dashboard screens — wired to real API const { useState: uSt, useEffect: uEf, useRef: uRf, useMemo: uMm } = React; // ───── Helpers shared in user screens ───── function useLoad(loader, deps = []) { const [data, setData] = uSt(null); const [loading, setLoading] = uSt(true); const [error, setError] = uSt(null); const reload = async () => { setLoading(true); setError(null); try { setData(await loader()); } catch (e) { setError(e.message); } finally { setLoading(false); } }; uEf(() => { reload(); }, deps); return { data, loading, error, reload, setData }; } // ───────── Overview ───────── function UserOverview({ goto }) { const toast = useToast(); const me = window.HDR_USER || {}; const { data: stats } = useLoad(() => window.HDR_API.stats(), []); const { data: logs } = useLoad(() => window.HDR_API.logs(20), []); const totals = stats?.totals || {}; const byEp = stats?.byEndpoint || []; const recent = (logs || []).slice(0, 6); return (

Namaste, {me.name?.split(' ')[0] || me.email?.split('@')[0]} 👋

{totals.reqs_30d || 0} requests in last 30 days. Real-time stats below.

Wallet} value={U.rupees(me.wallet_paise || 0).replace(/\.\d+$/, '')} unit={'.' + ((me.wallet_paise||0)%100).toString().padStart(2,'0')} spark={[20,22,19,24,30,28,33]} sparkColor="var(--accent)"/> Requests (30d)} value={U.num(totals.reqs_30d || 0)} spark={[8,12,10,18,15,22,28]}/> Spent (30d)} value={U.rupees(totals.spent_30d || 0).split('.')[0]} spark={[12,16,14,22,19,26,28]} sparkColor="var(--info)"/> Tokens (30d)} value={U.num(totals.tokens_30d || 0)} spark={[28,24,26,20,22,18,20]} sparkColor="var(--pos)"/>
Activity by endpoint
Last 30 days
{byEp.length === 0 ? (
No usage yet. { e.preventDefault(); goto('playground'); }} style={{color:'var(--accent)'}}>Open Playground →
) : (
{byEp.map(e => { const maxC = Math.max(...byEp.map(x => Number(x.c) || 0), 1); const pct = ((Number(e.c) / maxC) * 100).toFixed(0); return (
{e.endpoint}
{e.c} reqs · {U.rupees(e.cost)}
); })}
)}
Get started
Account created
₹50 signup bonus claimed
goto('keys')}>
3
Generate your first API key
goto('agents')}>
4
Create your first agent
Recent activity
{recent.length === 0 ? (
No activity yet
) : (
{recent.map(l => (
{l.model_alias || 'smart'} · {l.endpoint}
{U.rupees(l.cost_paise || 0)} {U.ago(new Date(l.created_at).getTime())}
))}
)}
); } // ───────── Playground ───────── function UserPlayground() { const [model, setModel] = uSt('smart'); const [agent, setAgent] = uSt(''); const [system, setSystem] = uSt('You are a helpful assistant.'); const [input, setInput] = uSt('Hello! Briefly introduce yourself.'); const [temp, setTemp] = uSt(0.7); const [maxTok, setMaxTok] = uSt(1024); const [topP, setTopP] = uSt(1.0); const [stopSeq, setStopSeq] = uSt(''); const [format, setFormat] = uSt('text'); const [wantButtons, setWantButtons] = uSt(false); const [wantDecision, setWantDecision] = uSt(false); const [busy, setBusy] = uSt(false); const [msgs, setMsgs] = uSt([]); const [showCode, setShowCode] = uSt(false); const { data: agents } = useLoad(() => window.HDR_API.listAgents(), []); const toast = useToast(); const buildBody = (extraInput) => { const messages = [ ...(system ? [{ role: 'system', content: system }] : []), ...msgs.map(m => ({ role: m.role, content: m.content })), ...(extraInput ? [{ role: 'user', content: extraInput }] : []), ]; const body = { model, messages, temperature: temp, max_tokens: maxTok, top_p: topP }; if (agent) body.agent = agent; if (stopSeq.trim()) body.stop = stopSeq.split(',').map(s => s.trim()).filter(Boolean); if (format === 'json') body.response_format = { type: 'json_object' }; if (wantButtons) body.buttons = true; if (wantDecision) body.decision = true; return body; }; const send = async (overrideInput) => { // Type-guard: button onClick passes the event, button-suggestion callbacks pass strings const inText = (typeof overrideInput === 'string' ? overrideInput : input).trim(); if (!inText || busy) return; const userMsg = { role: 'user', content: inText }; setMsgs(m => [...m, userMsg]); if (typeof overrideInput !== 'string') setInput(''); setBusy(true); try { const r = await window.HDR_API.chat(buildBody(inText)); if (r.error) throw new Error(r.error.message || r.error); const reply = r.choices[0].message.content; setMsgs(m => [...m, { role: 'assistant', content: reply, provider: model, cost: r.cost?.paise || 0, latency: r.latency_ms, tokens: r.usage?.total_tokens, buttons: r.buttons || null, decision: r.decision || null, }]); } catch (e) { toast('Failed: ' + (e.message || 'unknown'), 'neg'); } finally { setBusy(false); } }; const aliasInfo = C.aliases.find(a => a.slug === model); const aliasTag = aliasInfo?.tag || 'AI'; // Live cURL preview const curlPreview = `curl https://api.hidrogen.in/v1/chat/completions \\ -H "Authorization: Bearer hi_xxx" \\ -H "Content-Type: application/json" \\ -d '${JSON.stringify(buildBody(input), null, 2)}'`; return (

Playground

Test models, agents, and output formats. Costs deducted from your wallet at standard rates.

{/* Chat panel */}
{/* Top bar with model + agent + chain */}
Model
Agent
{aliasTag} {format === 'json' && JSON mode}
{/* Messages */}
{msgs.length === 0 && !busy && (
Start a conversation
Pick a model and agent, type a message, hit ⏎ or Cmd+Enter.
Cost shown after each reply.
)} {msgs.map((m, i) => (
{m.content}
{m.role === 'assistant' && ( <>
{m.provider} {m.latency && {m.latency}ms} {m.tokens && {m.tokens} tokens} {m.cost > 0 && {U.rupees(m.cost)}}
{/* Suggested buttons */} {Array.isArray(m.buttons) && m.buttons.length > 0 && (
{m.buttons.map((b, bi) => ( ))}
)} {/* Decision panel */} {m.decision && (
{m.decision.action || 'decision'} {typeof m.decision.confidence === 'number' && ( {Math.round(m.decision.confidence * 100)}% )}
{m.decision.reason && (
Why: {m.decision.reason}
)} {m.decision.next_step && (
Next: {m.decision.next_step}
)}
)} )}
))} {busy && (
● ● ●
)}
{/* Composer */}