feat: multi-tab instrument analysis

Each /instruments/:id navigation opens a persistent tab in the TabBar.
Multiple instruments can be open simultaneously — state is preserved when switching.

- TabsContext: adds instrumentIds[], openInstrument(), closeInstrument()
- TabBar: renders instrument tabs (teal, monospaced, TrendingUp icon) after static tabs, separated by a divider
- InstrumentDashboard: accepts instrumentIdProp so keep-alive instances use the right id regardless of URL
- App: InstrumentRoute registers the tab on navigation; InstrumentKeepAlive mounts one dashboard per open instrument; NormalRoutes hides on instrument paths (hidden div, Routes still fires)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-30 09:17:05 +02:00
parent 230ce3ff2b
commit 9a1e943be0
4 changed files with 146 additions and 45 deletions

View File

@@ -1202,8 +1202,8 @@ const PERIODS = [
{ key: '5y', label: '5Y' },
]
export default function InstrumentDashboard() {
const { id = 'SPY' } = useParams<{ id: string }>()
export default function InstrumentDashboard({ instrumentIdProp }: { instrumentIdProp?: string } = {}) {
const { id: paramId = 'EURUSD' } = useParams<{ id: string }>()
const navigate = useNavigate()
const [period, setPeriod] = useState('1y')
const [chartStyle, setChartStyle] = useState<'candles' | 'line'>('candles')
@@ -1226,7 +1226,8 @@ export default function InstrumentDashboard() {
const chartCanvasLeftRef = useRef<number>(0)
const [chartReady, setChartReady] = useState(0) // bump to trigger frise re-render
const instrumentId = id.toUpperCase()
// instrumentIdProp is set when mounted as a keep-alive tab (so URL params don't bleed across instances)
const instrumentId = (instrumentIdProp ?? paramId).toUpperCase()
useEffect(() => {
api.get('/instruments').then(r => setInstruments(r.data)).catch(() => {})