feat: new cockpit

This commit is contained in:
OpenSquared
2026-07-14 13:43:34 +02:00
parent 922bb76567
commit 7d348d5c25

View File

@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'
import { useMemo, useState, useRef, useLayoutEffect } from 'react'
import { useQuery } from '@tanstack/react-query'
import {
useGeoRiskScore, useAllQuotes,
@@ -144,6 +144,34 @@ export default function Dashboard() {
setRiskView(v); localStorage.setItem('dash_risk_view', v)
}
// Row height is dictated by the config-driven watchlist cards (Watchlist Radar for row 1,
// Options Lab for row 2) — the other cards in each row are capped to match, with internal scroll.
const watchlistCardRef = useRef<HTMLDivElement>(null)
const optionsLabCardRef = useRef<HTMLAnchorElement>(null)
const [row1Height, setRow1Height] = useState<number | undefined>(undefined)
const [row2Height, setRow2Height] = useState<number | undefined>(undefined)
// Floors avoid the row collapsing to near-zero while the master card is still loading
useLayoutEffect(() => {
const el = watchlistCardRef.current
if (!el) return
const measure = () => setRow1Height(Math.max(el.offsetHeight, 220))
measure()
const ro = new ResizeObserver(measure)
ro.observe(el)
return () => ro.disconnect()
}, [])
useLayoutEffect(() => {
const el = optionsLabCardRef.current
if (!el) return
const measure = () => setRow2Height(Math.max(el.offsetHeight, 180))
measure()
const ro = new ResizeObserver(measure)
ro.observe(el)
return () => ro.disconnect()
}, [])
const allPatterns: any[] = allPatternsData ?? []
const scoreMap = useMemo(() => {
@@ -251,10 +279,10 @@ export default function Dashboard() {
</div>
)}
{/* Top row */}
<div className="grid grid-cols-4 gap-4">
{/* Top row — height capped to Watchlist Radar's natural size (the only card driven by user config) */}
<div className="grid grid-cols-4 gap-4 items-start">
{/* Geo Risk */}
<div className="card col-span-1 flex flex-col">
<div className="card col-span-1 flex flex-col overflow-hidden" style={row1Height ? { height: row1Height } : undefined}>
<div className="flex items-center justify-between mb-2 shrink-0">
<div className="section-title flex items-center gap-1 mb-0">
<Globe className="w-3 h-3" /> Geopolitical Risk
@@ -309,8 +337,8 @@ export default function Dashboard() {
) : <div className="text-slate-500 text-xs">Backend required</div>}
</div>
{/* Watchlist Radar — its natural height (no cap) drives row 1's height */}
<div className="card col-span-1">
{/* Watchlist Radar — natural height (config-driven), measured and used to cap the other row-1 cards */}
<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">
@@ -398,7 +426,8 @@ export default function Dashboard() {
]
return (
<Link to="/macro" className="card col-span-1 flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<Link to="/macro" className="card col-span-1 flex flex-col overflow-y-auto hover:border-slate-600/60 transition-all cursor-pointer"
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" />
@@ -470,7 +499,7 @@ export default function Dashboard() {
})()}
{/* Economic Events — real Forex Factory feed, grouped by date */}
<div className="card col-span-1 flex flex-col">
<div className="card col-span-1 flex flex-col overflow-hidden" style={row1Height ? { height: row1Height } : undefined}>
<div className="section-title flex items-center gap-1 shrink-0"><Clock className="w-3 h-3" /> Economic Events</div>
{upcomingEventsGrouped.length > 0 ? (
<div className="mt-2 space-y-2 overflow-y-auto flex-1 min-h-0">
@@ -497,8 +526,8 @@ export default function Dashboard() {
</div>
</div>
{/* ── Command Center: Résumé Opérationnel ── */}
<div className="grid grid-cols-4 gap-3">
{/* ── Command Center: Résumé Opérationnel ── height capped to Options Lab's natural size (config-driven) ── */}
<div className="grid grid-cols-4 gap-3 items-start">
{/* PnL */}
{(() => {
@@ -539,7 +568,8 @@ export default function Dashboard() {
const pfNet = pf?.net_pnl ?? null
return (
<Link to="/journal" className="card flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<Link to="/journal" className="card flex flex-col overflow-y-auto hover:border-slate-600/60 transition-all cursor-pointer"
style={row2Height ? { height: row2Height } : undefined}>
<div className="flex items-center justify-between mb-1.5">
<span className="section-title mb-0 text-[10px]">📊 P&L</span>
<div className="flex items-center gap-1.5">
@@ -706,7 +736,8 @@ export default function Dashboard() {
.sort((a, b) => b.value - a.value)
return (
<Link to="/risk" className="card flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<Link to="/risk" className="card flex flex-col overflow-y-auto hover:border-slate-600/60 transition-all cursor-pointer"
style={row2Height ? { height: row2Height } : undefined}>
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">🛡 Risk</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
@@ -776,7 +807,8 @@ export default function Dashboard() {
const rollingVar: any[] = (snap?.full_result?.rolling_var ?? []).slice(-30)
return (
<Link to="/var" className="card flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<Link to="/var" className="card flex flex-col overflow-y-auto hover:border-slate-600/60 transition-all cursor-pointer"
style={row2Height ? { height: row2Height } : undefined}>
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">📉 VaR</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
@@ -850,7 +882,7 @@ export default function Dashboard() {
.sort((a: any, b: any) => Math.abs((b.iv_rank ?? 50) - 50) - Math.abs((a.iv_rank ?? 50) - 50))
return (
<Link to="/options" className="card flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<Link to="/options" ref={optionsLabCardRef} className="card flex flex-col hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">🧪 Options Lab</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />