From b4c9be93349210f89c477b53d5241cd2c05b5b1a Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sat, 28 Mar 2026 15:48:38 +0100 Subject: [PATCH] feat(ui): browser Back/Forward restores exchange selection via history state Each exchange selection (from table or correlation chain) pushes a browser history entry with the selected exchange in location.state. When the user navigates away (to agent details, app scope, etc.) and presses Back, the previous history entry is restored and the split view with the diagram reappears exactly as they left it. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/pages/Exchanges/ExchangesPage.tsx | 38 +++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/ui/src/pages/Exchanges/ExchangesPage.tsx b/ui/src/pages/Exchanges/ExchangesPage.tsx index e4f70187..9d6f702f 100644 --- a/ui/src/pages/Exchanges/ExchangesPage.tsx +++ b/ui/src/pages/Exchanges/ExchangesPage.tsx @@ -1,5 +1,5 @@ -import { useState, useMemo, useCallback, useRef } from 'react'; -import { useNavigate } from 'react-router'; +import { useState, useMemo, useCallback, useRef, useEffect } from 'react'; +import { useNavigate, useLocation } from 'react-router'; import { useGlobalFilters } from '@cameleer/design-system'; import { useExecutionDetail } from '../../api/queries/executions'; import { useDiagramByRoute } from '../../api/queries/diagrams'; @@ -14,20 +14,42 @@ import Dashboard from '../Dashboard/Dashboard'; import type { SelectedExchange } from '../Dashboard/Dashboard'; export default function ExchangesPage() { - const [selected, setSelected] = useState(null); + const navigate = useNavigate(); + const location = useLocation(); + + // Restore selection from browser history state (enables Back/Forward) + const stateSelected = (location.state as any)?.selectedExchange as SelectedExchange | undefined; + const [selected, setSelectedInternal] = useState(stateSelected ?? null); + + // Sync from history state when the user navigates Back/Forward + useEffect(() => { + const restored = (location.state as any)?.selectedExchange as SelectedExchange | undefined; + setSelectedInternal(restored ?? null); + }, [location.state]); + const [splitPercent, setSplitPercent] = useState(50); const containerRef = useRef(null); + // Select an exchange: push a history entry so Back restores the previous state const handleExchangeSelect = useCallback((exchange: SelectedExchange) => { - setSelected(exchange); - }, []); + setSelectedInternal(exchange); + navigate(location.pathname + location.search, { + state: { ...location.state, selectedExchange: exchange }, + }); + }, [navigate, location.pathname, location.search, location.state]); + // Select a correlated exchange: push another history entry const handleCorrelatedSelect = useCallback((executionId: string, applicationName: string, routeId: string) => { - setSelected({ executionId, applicationName, routeId }); - }, []); + const exchange = { executionId, applicationName, routeId }; + setSelectedInternal(exchange); + navigate(location.pathname + location.search, { + state: { ...location.state, selectedExchange: exchange }, + }); + }, [navigate, location.pathname, location.search, location.state]); + // Clear selection: push a history entry without selection (so Back returns to selected state) const handleClearSelection = useCallback(() => { - setSelected(null); + setSelectedInternal(null); }, []); const handleSplitterDown = useCallback((e: React.PointerEvent) => {