Compare commits

..

7 Commits

Author SHA1 Message Date
Gaurav Tewari
b76790ce85 fix: ux issue (empty menu items + refetch on scroll) 2026-10-21 17:10:02 +05:30
Gaurav Tewari
af6b5258b9 chore: minor ux improvements 2026-10-21 17:10:02 +05:30
Gaurav Tewari
a068a6702d feat(trace-details): add AI thread view 2026-10-21 17:10:02 +05:30
Gaurav Tewari
cfd78e086e refactor: review changes 2026-10-21 17:07:54 +05:30
Gaurav Tewari
b4d4a370d3 feat(trace-details): overview and thread tabs
Assisted-by: Claude Opus 5.5
2026-10-21 16:30:28 +05:30
Gaurav Tewari
895434012b refactor(trace-details): lift header and store sync into page shell
Assisted-by: Claude Opus 5.5
2026-10-21 16:29:36 +05:30
Gaurav Tewari
04f2dc98ad refactor(trace-details): read search params at call time
The router's search goes stale once nuqs writes to the URL,
so span selection and links read the live URL instead.

Assisted-by: Claude Opus 5.5
2026-10-21 16:28:40 +05:30
3 changed files with 17 additions and 10 deletions

View File

@@ -68,10 +68,13 @@ function TraceDetailsHeader({
const isOverview = tab === TraceDetailsTab.Overview;
// Overview-only panels must not linger over another tab.
// The filter query lives in Filters' local state, which unmounts with the
// Overview tab; drop its result too so both start empty on return.
useEffect(() => {
setIsPreviewFieldsOpen(false);
setIsAnalyticsOpen(false);
}, [tab]);
onFilteredSpansChange([], false);
}, [tab, onFilteredSpansChange]);
const previewFields = useTraceStore((s) => s.previewFields);
const setPreviewFields = useTraceStore((s) => s.setPreviewFields);

View File

@@ -383,6 +383,16 @@ describe('TraceDetailsHeader – tabs', () => {
expect(screen.getByText(metadataText)).toBeInTheDocument();
});
it('clears the filter result when switching to the Thread tab', async () => {
const user = userEvent.setup({ delay: null });
renderHeader();
baseProps.onFilteredSpansChange.mockClear();
await user.click(screen.getByTestId('trace-details-tab-thread'));
expect(baseProps.onFilteredSpansChange).toHaveBeenCalledWith([], false);
});
it('closes the Analytics panel when switching to the Thread tab', async () => {
const user = userEvent.setup({ delay: null });
renderHeader();

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import { TraceDetailsTab } from './constants';
import { useTraceDetailsTab } from './hooks/useTraceDetailsTab';
@@ -13,6 +13,8 @@ function TraceDetailsV3(): JSX.Element {
const [filteredSpanIds, setFilteredSpanIds] = useState<string[]>([]);
const [isFilterActive, setIsFilterActive] = useState(false);
const [hasTraceData, setHasTraceData] = useState(false);
// Tracks which traceId the load event already fired for, so navigating
// between traces (the route component stays mounted) re-fires it once each.
const dataLoadedFiredForRef = useRef<string>('');
const handleFilteredSpansChange = useCallback(
@@ -23,14 +25,6 @@ function TraceDetailsV3(): JSX.Element {
[],
);
// The filter query lives in Filters' local state, which unmounts with the
// Overview tab; drop its result too so both start empty on return.
useEffect(() => {
if (!isOverview) {
handleFilteredSpansChange([], false);
}
}, [isOverview, handleFilteredSpansChange]);
return (
<TraceDetailsWrapper>
<TraceDetailsHeader