feat: new cockpit
This commit is contained in:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user