89 lines
4.1 KiB
TypeScript
89 lines
4.1 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { History as HistoryIcon, RefreshCw } from 'lucide-react';
|
|
|
|
const History: React.FC = () => {
|
|
const [runs, setRuns] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const fetchHistory = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await fetch('/api/sessions');
|
|
const data = await res.json();
|
|
if (data.ok) {
|
|
// Sort by descending update time (assuming default or simple sort needed)
|
|
const sortedRuns = (data.runs || []).sort((a: any, b: any) => {
|
|
return new Date(b.updatedAt || 0).getTime() - new Date(a.updatedAt || 0).getTime();
|
|
});
|
|
setRuns(sortedRuns);
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to fetch history', e);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchHistory();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="fade-in">
|
|
<section className="panel">
|
|
<div className="panel-header">
|
|
<h2 className="panel-title">
|
|
<HistoryIcon size={18} className="text-secondary" /> Run History
|
|
</h2>
|
|
<button className="primary" onClick={fetchHistory} disabled={loading}>
|
|
<RefreshCw size={14} className={loading ? 'animate-spin' : ''} /> Refresh
|
|
</button>
|
|
</div>
|
|
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-left border-collapse">
|
|
<thead>
|
|
<tr className="border-b border-color text-subtle">
|
|
<th className="p-3 font-medium">Session ID</th>
|
|
<th className="p-3 font-medium">Status</th>
|
|
<th className="p-3 font-medium">Attempts</th>
|
|
<th className="p-3 font-medium">Updated</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{runs.length === 0 ? (
|
|
<tr>
|
|
<td className="p-3" colSpan={4}>
|
|
<div className="text-center py-8 text-subtle">
|
|
{loading ? 'Loading...' : 'No run history available.'}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
runs.map((run: any) => {
|
|
const dateStr = run.updatedAt ? new Date(run.updatedAt).toLocaleString() : 'N/A';
|
|
let statusColor = 'text-subtle';
|
|
if (run.status === 'success') statusColor = 'text-success';
|
|
if (run.status === 'failure' || run.status === 'cancelled') statusColor = 'text-danger';
|
|
if (run.status === 'validation_fail') statusColor = 'text-warning';
|
|
|
|
return (
|
|
<tr key={run.id} className="border-b border-color hover:bg-highlight transition-colors">
|
|
<td className="p-3 font-mono text-sm">{run.sessionId || 'N/A'}</td>
|
|
<td className={`p-3 font-medium ${statusColor}`}>{run.status || 'unknown'}</td>
|
|
<td className="p-3">{run.attempts ?? 0}</td>
|
|
<td className="p-3 text-sm text-subtle">{dateStr}</td>
|
|
</tr>
|
|
);
|
|
})
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default History;
|