solvox / src /renderer /pages /HistoryPage.tsx
muthuk1's picture
🎨 Complete Coinbase design system rebuild: white canvas, single blue accent, pill CTAs, editorial spacing, hairline borders, dark hero bands, asset rows, mono numbers, 96px section rhythm
5f5514e verified
import React, { useState, useEffect } from 'react';
export default function HistoryPage() {
const [txs, setTxs] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [query, setQuery] = useState('');
const [rag, setRag] = useState<any[]>([]);
const [searching, setSearching] = useState(false);
useEffect(() => { load(); }, []);
const load = async () => { setLoading(true); try { if (window.solvox) { const r = await window.solvox.wallet.getHistory(20); if (r.success) setTxs(r.history || []); } } catch {} setLoading(false); };
const search = async () => { if (!query.trim()) return; setSearching(true); try { if (window.solvox) { const r = await window.solvox.rag.search(query, 'transaction'); if (r.success) setRag(r.results || []); } } catch {} setSearching(false); };
return (
<div className="max-w-content mx-auto px-8 py-section">
<div className="flex items-center justify-between mb-6">
<h2 className="display-text text-title-lg text-ink">Transactions</h2>
<button onClick={load} className="btn-secondary text-body-sm py-2 px-3">Refresh</button>
</div>
{/* AI Search */}
<div className="bg-surface-soft rounded-xl p-4 mb-8">
<div className="flex gap-2">
<input value={query} onChange={e => setQuery(e.target.value)} onKeyDown={e => e.key === 'Enter' && search()}
placeholder='Search with AI — "last payment to Alice"' className="search-pill flex-1 text-body-sm" />
<button onClick={search} disabled={searching} className="btn-primary text-body-sm py-2 px-4 disabled:opacity-50">{searching ? '…' : 'Search'}</button>
</div>
<div className="text-caption text-muted mt-2 flex items-center gap-1.5">
<div className="w-1 h-1 rounded-full bg-primary" />
Semantic search via @qvac/embed-llamacpp — 100% local
</div>
{rag.length > 0 && (
<div className="mt-3 space-y-2">
<div className="text-caption-strong text-primary uppercase tracking-wider">AI Results</div>
{rag.map((r, i) => (
<div key={i} className="bg-canvas rounded-lg p-3 border border-hairline">
<div className="text-body-sm text-ink">{r.text}</div>
<div className="text-caption text-muted mt-1 number-mono">{(r.score * 100).toFixed(0)}% match</div>
</div>
))}
</div>
)}
</div>
{/* Table */}
<div className="card" style={{ padding: 0 }}>
{loading ? (
<div className="p-12 text-center text-body text-muted">Loading…</div>
) : txs.length === 0 ? (
<div className="p-12 text-center">
<div className="text-title-md text-ink mb-1">No transactions yet</div>
<div className="text-body-sm text-muted">Send or receive tokens to see history.</div>
</div>
) : (
<div>
{txs.map((tx, i) => (
<div key={i} className="asset-row px-5">
<div className={`w-8 h-8 rounded-full flex items-center justify-center text-caption-strong mr-3 ${tx.status === 'success' ? 'bg-semantic-up/10 text-semantic-up' : 'bg-semantic-down/10 text-semantic-down'}`}>
{tx.status === 'success' ? '✓' : '✗'}
</div>
<div className="flex-1 min-w-0">
<div className="text-body-sm font-mono text-ink truncate">{tx.signature?.slice(0, 20)}…</div>
<div className="text-caption text-muted">{tx.timestamp ? new Date(tx.timestamp).toLocaleString() : 'Pending'}</div>
</div>
<span className={`badge-pill text-[10px] mr-3 ${tx.status === 'success' ? 'badge-pill-green' : 'badge-pill-red'}`}>{tx.status}</span>
<a href={`https://solscan.io/tx/${tx.signature}`} target="_blank" rel="noopener noreferrer" className="btn-text text-body-sm py-0 px-1">View →</a>
</div>
))}
</div>
)}
</div>
</div>
);
}