stemedb/latent/dashboard/app/page.tsx
jordan b3e8a9a058 feat: Multi-application expansion with chaos testing and community UI
Major additions:
- Community Next.js app (port 18187) for browsing claims with API docs
- stemedb-chaos crate: Fault injection, chaos testing, CRDT properties
- Latent ingestion system: Reddit/FDA ingesters with ADK-Go agents
- Disputed claims handling: Manual review workflows and validation
- Aphoria security scanner: New extractors (SQL injection, command
  injection, weak crypto, TLS version), policy-based ignores, UAT reports
- Docker infrastructure: Dockerfile, docker-compose.yml for full stack
- VulnBank demo: Intentionally vulnerable multi-language test corpus

SDK & API enhancements:
- Source registry handlers for tracking data provenance
- Metrics endpoint
- Skeptic filtering improvements

Code quality:
- Split 14 large files (>500 lines) into focused modules
- All files now under 500-line limit per project guidelines

Documentation:
- Chaos testing guide, circuit breakers, observability docs
- Phase 7 UAT documentation updates
- Martin Kleppmann technical writer agent

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 01:24:14 -07:00

160 lines
7.1 KiB
TypeScript

import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { AlertTriangle, CheckCircle, Activity, ArrowUpRight } from "lucide-react"
async function getData() {
// In a real app, this would be an API call or DB query
// For the prototype, we load the JSON generated by the python engine
// Simulating fetch delay
await new Promise(resolve => setTimeout(resolve, 100))
// Use environment variable with localhost fallback for development
const apiUrl = process.env.LATENT_API_URL || 'http://localhost:3000'
const res = await fetch(`${apiUrl}/data.json`, { cache: 'no-store' })
if (!res.ok) throw new Error('Failed to fetch data')
return res.json()
}
export default async function Dashboard() {
const signals = await getData()
return (
<div className="min-h-screen bg-slate-950 text-slate-100 p-8 font-mono">
{/* Header */}
<div className="max-w-6xl mx-auto mb-12 border-b border-slate-800 pb-6">
<div className="flex justify-between items-end">
<div>
<h1 className="text-4xl font-bold tracking-tight text-white mb-2">LATENT <span className="text-emerald-500 text-sm align-top">v1.0</span></h1>
<p className="text-slate-400">Epistemic Divergence Monitor // Pharma Sector</p>
</div>
<div className="text-right">
<div className="text-xs text-slate-500 uppercase tracking-widest mb-1">Status</div>
<div className="flex items-center gap-2 text-emerald-400">
<span className="relative flex h-3 w-3">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-3 w-3 bg-emerald-500"></span>
</span>
LIVE MONITORING
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Left Col: High Level Stats */}
<div className="space-y-6">
<Card className="bg-slate-900 border-slate-800">
<CardHeader>
<CardTitle className="text-slate-400 text-sm uppercase tracking-wider">Active Molecules</CardTitle>
</CardHeader>
<CardContent>
<div className="text-4xl font-bold text-white">3</div>
<p className="text-xs text-slate-500 mt-2">Semaglutide, Tirzepatide, Liraglutide</p>
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<CardHeader>
<CardTitle className="text-slate-400 text-sm uppercase tracking-wider">Social Volume</CardTitle>
</CardHeader>
<CardContent>
<div className="text-4xl font-bold text-white">8,492</div>
<p className="text-xs text-slate-500 mt-2">Posts analyzed (Last 30d)</p>
</CardContent>
</Card>
<div className="p-4 bg-amber-950/30 border border-amber-900/50 rounded-lg">
<h3 className="text-amber-500 font-bold flex items-center gap-2 mb-2">
<AlertTriangle className="h-4 w-4" />
Alpha Signal Detected
</h3>
<p className="text-sm text-amber-200/80">
Semaglutide shows a <strong>0.88 divergence</strong> on "Gastroparesis". Signal is absent from FDA label but high volume in Tier 5 sources.
</p>
</div>
</div>
{/* Right Col: The Heatmap */}
<div className="lg:col-span-2 space-y-6">
<h2 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
<Activity className="h-5 w-5 text-indigo-400" />
Signal Divergence Feed
</h2>
<div className="grid gap-4">
{signals.map((signal: any, idx: number) => (
<div key={idx} className="group relative bg-slate-900 hover:bg-slate-800 border border-slate-800 p-6 rounded-xl transition-all duration-200">
<div className="flex justify-between items-start mb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<h3 className="text-lg font-bold text-white capitalize">{signal.molecule}</h3>
<span className="text-slate-600 text-sm">//</span>
<span className="text-indigo-300 font-medium">{signal.signal}</span>
</div>
<div className="flex gap-2 mt-2">
<Badge variant={signal.status === 'LATENT_SIGNAL' ? 'destructive' : 'secondary'}>
{signal.status.replace('_', ' ')}
</Badge>
<Badge variant="outline" className="text-slate-400 border-slate-700">
Vol: {signal.volume}
</Badge>
</div>
</div>
<div className="text-right">
<div className="text-xs text-slate-500 uppercase tracking-wider mb-1">Divergence Score</div>
<div className={`text-3xl font-bold ${getScoreColor(signal.divergence_score)}`}>
{signal.divergence_score.toFixed(2)}
</div>
</div>
</div>
{/* Progress Bar Visual */}
<div className="w-full bg-slate-950 h-2 rounded-full overflow-hidden mb-4">
<div
className={`h-full ${getBarColor(signal.divergence_score)}`}
style={{ width: `${signal.divergence_score * 100}%` }}
></div>
</div>
<div className="flex justify-between items-center text-sm text-slate-400 border-t border-slate-800 pt-4 mt-4">
<div className="flex gap-4">
<div>
<span className="block text-xs text-slate-600 uppercase">FDA Status</span>
<span className={signal.regulatory_status.includes('Absent') ? 'text-red-400' : 'text-emerald-400'}>
{signal.regulatory_status}
</span>
</div>
<div>
<span className="block text-xs text-slate-600 uppercase">Social Trend</span>
<span className="text-white">Rising (+12%)</span>
</div>
</div>
<button className="text-indigo-400 hover:text-indigo-300 flex items-center gap-1 text-xs uppercase tracking-widest font-bold">
Investigate <ArrowUpRight className="h-3 w-3" />
</button>
</div>
</div>
))}
</div>
</div>
</div>
</div>
)
}
function getScoreColor(score: number) {
if (score > 0.7) return "text-red-500"
if (score > 0.4) return "text-amber-500"
return "text-emerald-500"
}
function getBarColor(score: number) {
if (score > 0.7) return "bg-red-500"
if (score > 0.4) return "bg-amber-500"
return "bg-emerald-500"
}