Files
stonks-oracle/frontend/src/pages/GlobalEventDetail.tsx
T

110 lines
4.6 KiB
TypeScript

import { useParams, useNavigate } from '@tanstack/react-router';
import { useGlobalEvent } from '../api/hooks';
import { StatusBadge, ConfidenceBar, LoadingSpinner, Card } from '../components/ui';
import { DataTable, type Column } from '../components/DataTable';
import type { MacroImpactRecord } from '../api/hooks';
const severityColors: Record<string, string> = {
critical: 'bg-red-900/40 text-red-400 border-red-700/50',
high: 'bg-orange-900/40 text-orange-400 border-orange-700/50',
moderate: 'bg-yellow-900/40 text-yellow-400 border-yellow-700/50',
low: 'bg-green-900/40 text-green-400 border-green-700/50',
};
const impactCols: Column<MacroImpactRecord>[] = [
{ key: 'ticker', header: 'Ticker', render: (r) => <span className="font-mono font-semibold text-brand-300">{r.ticker}</span> },
{ key: 'macro_impact_score', header: 'Impact Score', render: (r) => <ConfidenceBar value={r.macro_impact_score} /> },
{ key: 'impact_direction', header: 'Direction', render: (r) => <StatusBadge status={r.impact_direction} /> },
{
key: 'contributing_factors',
header: 'Contributing Factors',
render: (r) => (
<div className="flex flex-wrap gap-1">
{r.contributing_factors.map((f, i) => (
<span key={i} className="rounded bg-surface-800 px-1.5 py-0.5 text-[10px] text-gray-400">{f}</span>
))}
</div>
),
},
{ key: 'confidence', header: 'Confidence', render: (r) => <ConfidenceBar value={r.confidence} /> },
{ key: 'computed_at', header: 'Computed', render: (r) => <span className="text-xs">{new Date(r.computed_at).toLocaleString()}</span> },
];
export function GlobalEventDetailPage() {
const { id } = useParams({ from: '/macro/events/$id' });
const navigate = useNavigate();
const { data: event, isLoading } = useGlobalEvent(id);
if (isLoading || !event) return <LoadingSpinner />;
const sevCls = severityColors[event.severity] ?? 'bg-gray-800/40 text-gray-400 border-gray-700/50';
return (
<div className="space-y-6">
<div className="flex items-center gap-3">
<h1 className="text-xl font-semibold text-gray-100">Global Event</h1>
<span className={`inline-block rounded-full border px-2 py-0.5 text-xs font-medium ${sevCls}`}>{event.severity}</span>
<span className="rounded bg-surface-800 px-2 py-0.5 text-xs text-gray-400">{event.estimated_duration.replace(/_/g, ' ')}</span>
</div>
<Card>
<h2 className="mb-2 text-sm font-medium text-gray-400">Summary</h2>
<p className="text-sm text-gray-200">{event.summary}</p>
</Card>
<Card>
<dl className="grid grid-cols-2 gap-x-8 gap-y-3 text-sm sm:grid-cols-4">
<div>
<dt className="text-gray-500">Impact Types</dt>
<dd className="flex flex-wrap gap-1 mt-1">
{event.event_types.map((t) => (
<span key={t} className="rounded bg-surface-800 px-1.5 py-0.5 text-[10px] text-gray-300">{t.replace(/_/g, ' ')}</span>
))}
</dd>
</div>
<div>
<dt className="text-gray-500">Regions</dt>
<dd className="flex flex-wrap gap-1 mt-1">
{event.affected_regions.map((r) => (
<span key={r} className="rounded bg-surface-800 px-1.5 py-0.5 text-[10px] text-gray-300">{r}</span>
))}
</dd>
</div>
<div>
<dt className="text-gray-500">Sectors</dt>
<dd className="flex flex-wrap gap-1 mt-1">
{event.affected_sectors.map((s) => (
<span key={s} className="rounded bg-surface-800 px-1.5 py-0.5 text-[10px] text-gray-300">{s}</span>
))}
</dd>
</div>
<div>
<dt className="text-gray-500">Confidence</dt>
<dd><ConfidenceBar value={event.confidence} /></dd>
</div>
</dl>
</Card>
{event.key_facts && event.key_facts.length > 0 && (
<Card>
<h2 className="mb-2 text-sm font-medium text-gray-400">Key Facts</h2>
<ul className="ml-4 list-disc text-sm text-gray-300">
{event.key_facts.map((f, i) => <li key={i}>{f}</li>)}
</ul>
</Card>
)}
<Card>
<h2 className="mb-3 text-sm font-medium text-gray-400">Affected Companies ({event.impacts?.length ?? 0})</h2>
<DataTable<MacroImpactRecord>
data={event.impacts ?? []}
columns={impactCols}
keyField="id"
onRowClick={(row) => navigate({ to: '/companies/$id', params: { id: row.company_id } })}
filterFn={(row, q) => row.ticker.toLowerCase().includes(q.toLowerCase())}
/>
</Card>
</div>
);
}