Files
blackroad-os-prism-console/app/ledger/page.tsx
Alexa Louise 1655047a04 Add Agents, Intents, and Ledger pages to Prism Console
- Add /agents page with agent cards showing status, type, trust score
- Add /intents page to track declared intentions
- Add /ledger page with immutable event log display
- Update layout with navigation menu
- All pages fetch from api.blackroad.io

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 19:01:07 -06:00

119 lines
3.2 KiB
TypeScript

import { Suspense } from 'react';
interface LedgerEntry {
id: string;
timestamp: string;
agentId: string;
action: string;
target?: string;
hash: string;
previousHash?: string;
data: Record<string, any>;
}
async function fetchLedger(): Promise<LedgerEntry[]> {
try {
const res = await fetch('https://api.blackroad.io/ledger', {
next: { revalidate: 5 },
});
if (!res.ok) return [];
const data = await res.json();
return data.entries || [];
} catch (error) {
console.error('Failed to fetch ledger:', error);
return [];
}
}
function LedgerEntryCard({ entry }: { entry: LedgerEntry }) {
return (
<div className="card-surface p-4 font-mono text-sm">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-3">
<span className="text-blue-400">{entry.action}</span>
{entry.target && (
<>
<span className="text-gray-600"></span>
<span className="text-gray-400">{entry.target}</span>
</>
)}
</div>
<div className="mt-2 text-xs text-gray-500">
<div>Agent: {entry.agentId}</div>
<div>Hash: {entry.hash.substring(0, 16)}...</div>
{entry.previousHash && (
<div>Prev: {entry.previousHash.substring(0, 16)}...</div>
)}
</div>
{Object.keys(entry.data).length > 0 && (
<details className="mt-3">
<summary className="text-gray-400 cursor-pointer hover:text-gray-300">
Data ({Object.keys(entry.data).length} fields)
</summary>
<pre className="mt-2 text-xs text-gray-500 bg-black/30 p-2 rounded overflow-x-auto">
{JSON.stringify(entry.data, null, 2)}
</pre>
</details>
)}
</div>
<div className="text-xs text-gray-500 text-right">
{new Date(entry.timestamp).toLocaleString()}
</div>
</div>
</div>
);
}
async function LedgerList() {
const entries = await fetchLedger();
if (entries.length === 0) {
return (
<div className="card-surface p-8 text-center">
<p className="text-gray-400">No ledger entries</p>
<p className="text-sm text-gray-500 mt-2">
The immutable record will appear here
</p>
</div>
);
}
return (
<div className="flex flex-col gap-2">
{entries.map((entry) => (
<LedgerEntryCard key={entry.id} entry={entry} />
))}
</div>
);
}
export default function LedgerPage() {
return (
<div className="flex flex-col gap-6">
<section>
<h1 className="text-2xl font-bold text-white">Ledger</h1>
<p className="text-gray-400 mt-1">
Immutable event log with PS-SHA hash chain
</p>
<p className="text-sm text-gray-500 mt-2">
"The record is sacred."
</p>
</section>
<Suspense
fallback={
<div className="card-surface p-8 text-center text-gray-400">
Loading ledger...
</div>
}
>
<LedgerList />
</Suspense>
</div>
);
}