1- import { memo , useCallback , useEffect , useMemo , useState } from 'react' ;
1+ import { memo , useCallback , useEffect } from 'react' ;
22import { css } from '@emotion/react' ;
33import styled from '@emotion/styled' ;
44
5- import { Container , Flex } from '@sentry/scraps/layout ' ;
6- import { TabList , TabPanels , Tabs } from '@sentry/scraps/tabs ' ;
5+ import { Button } from '@sentry/scraps/button ' ;
6+ import { Flex } from '@sentry/scraps/layout ' ;
77
8- import { EmptyMessage } from 'sentry/components/emptyMessage' ;
9- import { DrawerBody , DrawerHeader } from 'sentry/components/globalDrawer/components' ;
10- import { LoadingIndicator } from 'sentry/components/loadingIndicator' ;
8+ import {
9+ DrawerBody ,
10+ DrawerHeader ,
11+ useDrawerContentContext ,
12+ } from 'sentry/components/globalDrawer/components' ;
13+ import { IconClose } from 'sentry/icons' ;
1114import { t } from 'sentry/locale' ;
1215import { trackAnalytics } from 'sentry/utils/analytics' ;
1316import type { ConversationDrawerOpenSource } from 'sentry/utils/analytics/conversationsAnalyticsEvents' ;
1417import { useOrganization } from 'sentry/utils/useOrganization' ;
15- import { AISpanList } from 'sentry/views/insights/pages/agents/components/aiSpanList' ;
16- import { getDefaultSelectedNode } from 'sentry/views/insights/pages/agents/utils/getDefaultSelectedNode' ;
17- import type { AITraceSpanNode } from 'sentry/views/insights/pages/agents/utils/types' ;
1818import { ConversationSummary } from 'sentry/views/insights/pages/conversations/components/conversationSummary' ;
19- import { MessagesPanel } from 'sentry/views/insights/pages/conversations/components/messagesPanel ' ;
19+ import { ConversationViewContent } from 'sentry/views/insights/pages/conversations/components/conversationView ' ;
2020import {
2121 useConversation ,
2222 type UseConversationsOptions ,
2323} from 'sentry/views/insights/pages/conversations/hooks/useConversation' ;
24- import { useFocusedToolSpan } from 'sentry/views/insights/pages/conversations/hooks/useFocusedToolSpan' ;
2524import { useUrlConversationDrawer } from 'sentry/views/insights/pages/conversations/hooks/useUrlConversationDrawer' ;
26- import { extractMessagesFromNodes } from 'sentry/views/insights/pages/conversations/utils/conversationMessages' ;
2725import { useConversationDrawerQueryState } from 'sentry/views/insights/pages/conversations/utils/urlParams' ;
28- import { DEFAULT_TRACE_VIEW_PREFERENCES } from 'sentry/views/performance/newTraceDetails/traceState/tracePreferences' ;
29- import { TraceStateProvider } from 'sentry/views/performance/newTraceDetails/traceState/traceStateProvider' ;
30-
31- const LEFT_PANEL_WIDTH = 400 ;
32- const DETAILS_PANEL_WIDTH = 500 ;
33- const DRAWER_WIDTH = LEFT_PANEL_WIDTH + DETAILS_PANEL_WIDTH ;
3426
35- type ConversationTab = 'messages' | 'trace' ;
27+ const DRAWER_WIDTH = 900 ;
3628
3729interface UseConversationViewDrawerProps {
3830 onClose ?: ( ) => void ;
@@ -43,96 +35,49 @@ const ConversationDrawerContent = memo(function ConversationDrawerContent({
4335} : {
4436 conversation : UseConversationsOptions ;
4537} ) {
46- const organization = useOrganization ( ) ;
47- const { nodes, nodeTraceMap, isLoading, error} = useConversation ( conversation ) ;
38+ const { nodes, isLoading} = useConversation ( conversation ) ;
4839 const [ conversationDrawerQueryState , setConversationDrawerQueryState ] =
4940 useConversationDrawerQueryState ( ) ;
50- const selectedNodeKey = conversationDrawerQueryState . spanId ;
41+ const selectedSpanId = conversationDrawerQueryState . spanId ;
5142 const focusedTool = conversationDrawerQueryState . focusedTool ;
5243
53- useFocusedToolSpan ( {
54- nodes,
55- focusedTool,
56- isLoading,
57- onSpanFound : useCallback (
58- ( spanId : string ) => {
59- setConversationDrawerQueryState ( {
60- spanId,
61- focusedTool : null ,
62- } ) ;
63- } ,
64- [ setConversationDrawerQueryState ]
65- ) ,
66- } ) ;
67-
68- const handleSelectNode = useCallback (
69- ( node : AITraceSpanNode ) => {
44+ const handleSelectSpan = useCallback (
45+ ( spanId : string ) => {
7046 setConversationDrawerQueryState ( {
71- spanId : node . id ,
47+ spanId,
7248 focusedTool : null ,
7349 } ) ;
74- trackAnalytics ( 'conversations.drawer.span-select' , {
75- organization,
76- } ) ;
7750 } ,
78- [ setConversationDrawerQueryState , organization ]
51+ [ setConversationDrawerQueryState ]
7952 ) ;
8053
81- const defaultNodeId = useMemo ( ( ) => {
82- const messages = extractMessagesFromNodes ( nodes ) ;
83- const firstAssistant = messages . find ( m => m . role === 'assistant' ) ;
84- return firstAssistant ?. nodeId ?? getDefaultSelectedNode ( nodes ) ?. id ;
85- } , [ nodes ] ) ;
86-
87- const selectedNode = useMemo ( ( ) => {
88- return (
89- nodes . find ( node => node . id === selectedNodeKey ) ??
90- nodes . find ( node => node . id === defaultNodeId )
91- ) ;
92- } , [ nodes , selectedNodeKey , defaultNodeId ] ) ;
93-
94- useEffect ( ( ) => {
95- if ( isLoading || ! defaultNodeId || focusedTool ) {
96- return ;
97- }
98-
99- const isCurrentSpanValid =
100- selectedNodeKey && nodes . some ( node => node . id === selectedNodeKey ) ;
101-
102- if ( ! isCurrentSpanValid ) {
103- setConversationDrawerQueryState ( {
104- spanId : defaultNodeId ,
105- } ) ;
106- }
107- } , [
108- isLoading ,
109- defaultNodeId ,
110- selectedNodeKey ,
111- nodes ,
112- setConversationDrawerQueryState ,
113- focusedTool ,
114- ] ) ;
54+ const { onClose} = useDrawerContentContext ( ) ;
11555
11656 return (
11757 < Flex direction = "column" height = "100%" >
118- < DrawerHeader >
119- < ConversationSummary
120- nodes = { nodes }
121- conversationId = { conversation . conversationId }
122- isLoading = { isLoading }
123- />
124- </ DrawerHeader >
125- < StyledDrawerBody >
126- < TraceStateProvider initialPreferences = { DEFAULT_TRACE_VIEW_PREFERENCES } >
127- < ConversationView
58+ < StyledDrawerHeader hideCloseButton >
59+ < Flex flex = { 1 } justify = "space-between" align = "flex-start" >
60+ < ConversationSummary
12861 nodes = { nodes }
129- nodeTraceMap = { nodeTraceMap }
130- selectedNode = { selectedNode }
131- onSelectNode = { handleSelectNode }
62+ conversationId = { conversation . conversationId }
13263 isLoading = { isLoading }
133- error = { error }
13464 />
135- </ TraceStateProvider >
65+ < Button
66+ priority = "transparent"
67+ size = "xs"
68+ aria-label = { t ( 'Close Drawer' ) }
69+ icon = { < IconClose /> }
70+ onClick = { onClose }
71+ />
72+ </ Flex >
73+ </ StyledDrawerHeader >
74+ < StyledDrawerBody >
75+ < ConversationViewContent
76+ conversation = { conversation }
77+ selectedSpanId = { selectedSpanId }
78+ onSelectSpan = { handleSelectSpan }
79+ focusedTool = { focusedTool }
80+ />
13681 </ StyledDrawerBody >
13782 </ Flex >
13883 ) ;
@@ -200,106 +145,9 @@ export function useConversationViewDrawer({
200145 } ;
201146}
202147
203- function ConversationView ( {
204- nodes,
205- nodeTraceMap,
206- selectedNode,
207- onSelectNode,
208- isLoading,
209- error,
210- } : {
211- error : boolean ;
212- isLoading : boolean ;
213- nodeTraceMap : Map < string , string > ;
214- nodes : AITraceSpanNode [ ] ;
215- onSelectNode : ( node : AITraceSpanNode ) => void ;
216- selectedNode : AITraceSpanNode | undefined ;
217- } ) {
218- const organization = useOrganization ( ) ;
219- const [ activeTab , setActiveTab ] = useState < ConversationTab > ( 'messages' ) ;
220-
221- const handleTabChange = useCallback (
222- ( newTab : ConversationTab ) => {
223- if ( activeTab !== newTab ) {
224- trackAnalytics ( 'conversations.drawer.tab-switch' , {
225- organization,
226- fromTab : activeTab ,
227- toTab : newTab ,
228- } ) ;
229- }
230- setActiveTab ( newTab ) ;
231- } ,
232- [ organization , activeTab ]
233- ) ;
234-
235- if ( isLoading ) {
236- return (
237- < Flex justify = "center" align = "center" flex = "1" height = "100%" >
238- < LoadingIndicator size = { 32 } > { t ( 'Loading conversation...' ) } </ LoadingIndicator >
239- </ Flex >
240- ) ;
241- }
242-
243- if ( error ) {
244- return < EmptyMessage > { t ( 'Failed to load conversation' ) } </ EmptyMessage > ;
245- }
246-
247- if ( nodes . length === 0 ) {
248- return < EmptyMessage > { t ( 'No AI spans found in this conversation' ) } </ EmptyMessage > ;
249- }
250-
251- return (
252- < Flex flex = "1" minHeight = "0" >
253- < LeftPanel >
254- < StyledTabs
255- value = { activeTab }
256- onChange = { key => handleTabChange ( key as ConversationTab ) }
257- >
258- < Container paddingTop = "lg" borderBottom = "primary" >
259- < TabList >
260- < TabList . Item key = "messages" > { t ( 'Messages' ) } </ TabList . Item >
261- < TabList . Item key = "trace" > { t ( 'AI Spans' ) } </ TabList . Item >
262- </ TabList >
263- </ Container >
264- < Flex flex = "1" minHeight = "0" width = "100%" overflowX = "hidden" overflowY = "auto" >
265- < FullWidthTabPanels >
266- < TabPanels . Item key = "messages" >
267- < MessagesPanel
268- nodes = { nodes }
269- selectedNodeId = { selectedNode ?. id ?? null }
270- onSelectNode = { onSelectNode }
271- />
272- </ TabPanels . Item >
273- < TabPanels . Item key = "trace" >
274- < Container padding = "md lg md lg" >
275- < AISpanList
276- nodes = { nodes }
277- selectedNodeKey = { selectedNode ?. id ?? nodes [ 0 ] ?. id ?? '' }
278- onSelectNode = { onSelectNode }
279- compressGaps
280- />
281- </ Container >
282- </ TabPanels . Item >
283- </ FullWidthTabPanels >
284- </ Flex >
285- </ StyledTabs >
286- </ LeftPanel >
287- < DetailsPanel >
288- { selectedNode ?. renderDetails ( {
289- node : selectedNode ,
290- manager : null ,
291- onParentClick : ( ) => { } ,
292- onTabScrollToNode : ( ) => { } ,
293- organization,
294- replay : null ,
295- traceId : nodeTraceMap . get ( selectedNode . id ) ?? '' ,
296- hideNodeActions : true ,
297- initiallyCollapseAiIO : true ,
298- } ) }
299- </ DetailsPanel >
300- </ Flex >
301- ) ;
302- }
148+ const StyledDrawerHeader = styled ( DrawerHeader ) `
149+ padding-left: ${ p => p . theme . space . xl } ;
150+ ` ;
303151
304152const StyledDrawerBody = styled ( DrawerBody ) `
305153 padding: 0;
@@ -308,35 +156,3 @@ const StyledDrawerBody = styled(DrawerBody)`
308156 display: flex;
309157 flex-direction: column;
310158` ;
311-
312- const LeftPanel = styled ( 'div' ) `
313- flex: 1;
314- min-width: ${ LEFT_PANEL_WIDTH } px;
315- min-height: 0;
316- border-right: 1px solid ${ p => p . theme . tokens . border . primary } ;
317- display: flex;
318- flex-direction: column;
319- overflow: hidden;
320- ` ;
321-
322- const StyledTabs = styled ( Tabs ) `
323- min-height: 0;
324- ` ;
325-
326- const FullWidthTabPanels = styled ( TabPanels ) `
327- width: 100%;
328- padding: 0;
329-
330- > [role='tabpanel'] {
331- width: 100%;
332- }
333- ` ;
334-
335- const DetailsPanel = styled ( 'div' ) `
336- width: ${ DETAILS_PANEL_WIDTH } px;
337- min-width: ${ DETAILS_PANEL_WIDTH } px;
338- min-height: 0;
339- background-color: ${ p => p . theme . tokens . background . primary } ;
340- overflow-y: auto;
341- overflow-x: hidden;
342- ` ;
0 commit comments