feat: cockpit

This commit is contained in:
OpenSquared
2026-07-21 16:48:12 +02:00
parent 24e5e4b03b
commit b6e9b96dc4
10 changed files with 644 additions and 142 deletions

View File

@@ -122,16 +122,16 @@ function EcoEventRow({ ev, showActual }: { ev: any; showActual: boolean }) {
<div className="flex items-center gap-1.5 text-[10px]">
<span className="shrink-0">{CURRENCY_FLAGS[ev.currency] ?? ev.currency}</span>
<span className={clsx('w-1.5 h-1.5 rounded-full shrink-0', IMPACT_DOT[ev.impact] ?? 'bg-slate-400')} />
<span className="text-white truncate flex-1 line-clamp-1">{ev.event_name}</span>
<span className="text-slate-200 truncate flex-1 line-clamp-1 max-w-[108px]" title={ev.event_name}>{ev.event_name}</span>
{showActual && ev.actual_value ? (
<span className="text-[9px] font-mono shrink-0">
<span className="text-[10px] font-mono shrink-0">
<span className="text-white font-semibold">{ev.actual_value}</span>
{ev.forecast_value && <span className="text-slate-600"> /{ev.forecast_value}</span>}
{ev.forecast_value && <span className="text-slate-400"> /{ev.forecast_value}</span>}
</span>
) : ev.forecast_value ? (
<span className="text-[9px] font-mono text-slate-600 shrink-0">F {ev.forecast_value}</span>
<span className="text-[10px] font-mono text-slate-400 shrink-0">F {ev.forecast_value}</span>
) : null}
{ev.event_time && <span className="text-slate-700 text-[9px] shrink-0 font-mono">{ev.event_time}</span>}
{ev.event_time && <span className="text-slate-500 text-[9px] shrink-0 font-mono">{ev.event_time}</span>}
</div>
)
}
@@ -347,7 +347,7 @@ export default function Dashboard() {
<div className="section-title flex items-center gap-1 mb-0">
<Globe className="w-3 h-3" /> Geopolitical Risk
</div>
<Link to="/geo" className="flex items-center gap-0.5 text-[10px] text-slate-600 hover:text-slate-300 transition-colors">
<Link to="/geo" className="flex items-center gap-0.5 text-[10px] text-slate-400 hover:text-slate-300 transition-colors">
Geo news <ArrowUpRight className="w-2.5 h-2.5" />
</Link>
</div>
@@ -376,14 +376,16 @@ export default function Dashboard() {
<div className={clsx('h-2 rounded-full', gauge.bg)} style={{ width: `${riskScore.score}%` }} />
</div>
{riskScore.computed_at && (
<div className="mt-1.5 text-[9px] text-slate-600 shrink-0">
Score IA figé au dernier cycle ({formatTimeShort(riskScore.computed_at)})
{riskScore.rationale && <span className="text-slate-500"> · {riskScore.rationale}</span>}
<div className="mt-1.5 text-[9px] text-slate-400 shrink-0">
<span>AI Score frozen at last cycle ({formatTimeShort(riskScore.computed_at)})</span>
{riskScore.rationale && (
<p className="text-slate-500 line-clamp-2 mt-0.5" title={riskScore.rationale}>{riskScore.rationale}</p>
)}
</div>
)}
{topNews.length > 0 && (
<div className="mt-2.5 pt-2 border-t border-slate-700/30 flex flex-col flex-1 min-h-0">
<div className="flex items-center gap-1 text-[9px] text-slate-600 mb-1 shrink-0">
<div className="mt-2 pt-2 border-t border-slate-700/30 flex flex-col flex-1 min-h-0">
<div className="flex items-center gap-1 text-[9px] text-slate-400 mb-1 shrink-0">
<Newspaper className="w-2.5 h-2.5" /> Top news
</div>
<div className="space-y-1.5 overflow-y-auto flex-1 min-h-0">
@@ -410,7 +412,7 @@ export default function Dashboard() {
<div ref={watchlistCardRef} className="card col-span-1">
<div className="flex items-center justify-between mb-2">
<div className="section-title mb-0">📡 Watchlist Radar</div>
<Link to="/config" className="flex items-center gap-0.5 text-[10px] text-slate-600 hover:text-slate-300 transition-colors">
<Link to="/config" className="flex items-center gap-0.5 text-[10px] text-slate-400 hover:text-slate-300 transition-colors">
Manage <ArrowUpRight className="w-2.5 h-2.5" />
</Link>
</div>
@@ -419,7 +421,7 @@ export default function Dashboard() {
<ResponsiveContainer width="100%" height={130}>
<RadarChart data={watchlistRadarData}>
<PolarGrid stroke="#1e2d4d" />
<PolarAngleAxis dataKey="subject" tick={{ fill: '#64748b', fontSize: 9 }} />
<PolarAngleAxis dataKey="subject" tick={{ fill: '#94a3b8', fontSize: 9 }} />
<Radar dataKey="ref" stroke="#334155" strokeDasharray="3 3" fill="transparent" isAnimationActive={false} />
<Radar dataKey="value" stroke="#3b82f6" fill="#3b82f6" fillOpacity={0.25} />
</RadarChart>
@@ -427,9 +429,9 @@ export default function Dashboard() {
<div className="mt-1.5 pt-1.5 border-t border-slate-700/30 space-y-1">
{((watchlistQuotesData as any)?.items ?? []).map((it: any) => (
<div key={it.ticker} className="flex items-center justify-between text-[10px]">
<span className="text-slate-400 font-mono">{it.ticker}</span>
<span className="text-slate-300 font-mono">{it.ticker}</span>
<div className="flex items-center gap-2">
<span className="text-slate-500 font-mono">{fmtPrice(it.price)}</span>
<span className="text-slate-400 font-mono">{fmtPrice(it.price)}</span>
<span className={clsx('font-mono font-bold w-12 text-right', (it.change_pct ?? 0) >= 0 ? 'text-emerald-400' : 'text-red-400')}>
{it.change_pct != null ? `${it.change_pct >= 0 ? '+' : ''}${it.change_pct.toFixed(2)}%` : '—'}
</span>
@@ -499,7 +501,7 @@ export default function Dashboard() {
style={row1Height ? { height: row1Height } : undefined}>
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">🌐 Macro Regime</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
<ArrowUpRight className="w-3 h-3 text-slate-500" />
</div>
<div className={clsx('text-base font-bold mt-1', colorClass)}>
{macroInfo ? `${macroInfo.emoji} ${macroInfo.label}` : '—'}
@@ -512,7 +514,7 @@ export default function Dashboard() {
const isDom = key === dom
return (
<div key={key} className="flex items-center gap-1.5">
<span className={clsx('text-[9px] w-16 truncate', isDom ? 'text-slate-200 font-semibold' : 'text-slate-500')}>
<span className={clsx('text-[10px] w-16 truncate', isDom ? 'text-slate-200 font-semibold' : 'text-slate-400')}>
{REGIME_LABELS[key] ?? key}
</span>
<div className="flex-1 h-1.5 bg-dark-700 rounded-full overflow-hidden">
@@ -521,7 +523,7 @@ export default function Dashboard() {
style={{ width: `${pct}%` }}
/>
</div>
<span className={clsx('text-[9px] font-mono w-5 text-right', isDom ? 'text-blue-400' : 'text-slate-600')}>
<span className={clsx('text-[10px] font-mono w-5 text-right', isDom ? 'text-blue-400' : 'text-slate-400')}>
{score}
</span>
</div>
@@ -542,11 +544,11 @@ export default function Dashboard() {
const h = hint(val, chg)
return (
<div key={key} className="flex items-center gap-1.5">
<span className="text-[9px] text-slate-600 w-16 shrink-0">{label}</span>
<span className={clsx('text-[9px] font-mono font-semibold shrink-0', c)}>
<span className="text-[10px] text-slate-400 w-16 shrink-0">{label}</span>
<span className={clsx('text-[10px] font-mono font-semibold shrink-0', c)}>
{fmt(val, chg)}
</span>
<span className="text-[8px] text-slate-700 truncate">{h}</span>
<span className="text-[9px] text-slate-500 truncate">{h}</span>
</div>
)
})}
@@ -575,7 +577,7 @@ export default function Dashboard() {
{todayEvents.length > 0 && (
<div>
<div className="flex items-center gap-1.5 text-[9px] font-semibold text-slate-300 bg-dark-700/50 rounded px-1.5 py-0.5 mb-1">
Aujourd'hui <span className="badge-blue text-[8px] px-1 py-0">TODAY</span>
Today <span className="badge-blue text-[8px] px-1 py-0">TODAY</span>
</div>
<div className="space-y-1 pl-0.5">
{todayEvents.map((ev: any, i: number) => <EcoEventRow key={i} ev={ev} showActual />)}
@@ -593,7 +595,7 @@ export default function Dashboard() {
</div>
))}
</div>
) : <div className="text-slate-600 text-xs mt-2">No events this week</div>}
) : <div className="text-slate-500 text-xs mt-2">No events this week</div>}
</div>
</div>