import styled from '@emotion/styled'; import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import Link from 'sentry/components/links/link'; import {generateTraceTarget} from 'sentry/components/quickTrace/utils'; import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Event} from 'sentry/types'; import {trackAnalytics} from 'sentry/utils/analytics'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; import type {TimelineEvent} from './useTraceTimelineEvents'; interface TraceTimelineTooltipProps { event: Event; timelineEvents: TimelineEvent[]; } export function TraceTimelineTooltip({event, timelineEvents}: TraceTimelineTooltipProps) { const organization = useOrganization(); const location = useLocation(); const {projects} = useProjects({ slugs: [ ...timelineEvents.reduce((acc, cur) => acc.add(cur.project), new Set()), ], orgId: organization.slug, }); // TODO: should handling of current event + other events look different if (timelineEvents.length === 1 && timelineEvents[0].id === event.id) { return {t('You are here')}; } const filteredTimelineEvents = timelineEvents.filter( timelineEvent => timelineEvent.id !== event.id ); const displayYouAreHere = filteredTimelineEvents.length !== timelineEvents.length; const hasTitle = filteredTimelineEvents.length > 1 || displayYouAreHere; return ( {displayYouAreHere && {t('You are here')}} {hasTitle && {t('Around the same time')}} {filteredTimelineEvents.slice(0, 3).map(timelineEvent => { const project = projects.find(p => p.slug === timelineEvent.project); return ( { trackAnalytics('issue_details.issue_tab.trace_timeline_clicked', { organization, event_id: timelineEvent.id, group_id: `${timelineEvent['issue.id']}`, }); }} >
{project && ( )}
{timelineEvent.title} {timelineEvent.transaction ? timelineEvent.transaction : 'stack.function' in timelineEvent ? timelineEvent['stack.function'].at(-1) : null}
); })}
{filteredTimelineEvents.length > 3 && ( { trackAnalytics( 'issue_details.issue_tab.trace_timeline_more_events_clicked', { organization, num_hidden: filteredTimelineEvents.length - 3, } ); }} > {tn( 'View %s more event', 'View %s more events', filteredTimelineEvents.length - 3 )} )}
); } const UnstyledUnorderedList = styled('div')` display: flex; flex-direction: column; text-align: left; width: 220px; `; const EventItemsWrapper = styled('div')<{hasTitle: boolean}>` display: flex; flex-direction: column; padding: ${p => space(p.hasTitle ? 1 : 0.5)} ${space(0.5)} ${space(0.5)} ${space(0.5)}; `; const EventItemsTitle = styled('div')` padding-left: ${space(1)}; text-transform: uppercase; font-size: ${p => p.theme.fontSizeExtraSmall}; font-weight: 600; color: ${p => p.theme.subText}; `; const YouAreHere = styled('div')` padding: ${space(1)} ${space(2)}; text-align: center; font-size: ${p => p.theme.fontSizeMedium}; `; const YouAreHereItem = styled('div')` padding: ${space(1)} ${space(2)}; text-align: center; border-bottom: 1px solid ${p => p.theme.innerBorder}; font-size: ${p => p.theme.fontSizeMedium}; `; const EventItem = styled(Link)` display: grid; grid-template-columns: max-content auto; color: ${p => p.theme.textColor}; gap: ${space(1)}; width: 100%; padding: ${space(1)} ${space(1)} ${space(0.5)} ${space(1)}; border-radius: ${p => p.theme.borderRadius}; font-size: ${p => p.theme.fontSizeSmall}; &:hover { background-color: ${p => p.theme.surface200}; color: ${p => p.theme.textColor}; } `; const EventTitleWrapper = styled('div')` width: 100%; overflow: hidden; line-height: 1.2; `; const EventTitle = styled('div')` ${p => p.theme.overflowEllipsis}; font-weight: 600; `; const EventDescription = styled('div')` ${p => p.theme.overflowEllipsis}; direction: rtl; `; const TraceItem = styled('div')` padding: ${space(1)} ${space(1.5)}; border-radius: ${p => p.theme.borderRadius}; border-top: 1px solid ${p => p.theme.innerBorder}; `;