import {useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import Breadcrumbs from 'sentry/components/breadcrumbs'; import ButtonBar from 'sentry/components/buttonBar'; import DiscoverButton from 'sentry/components/discoverButton'; import {HighlightsIconSummary} from 'sentry/components/events/highlights/highlightsIconSummary'; import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; import * as Layout from 'sentry/components/layouts/thirds'; import Placeholder from 'sentry/components/placeholder'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {EventTransaction} from 'sentry/types/event'; import type {Organization} from 'sentry/types/organization'; import {trackAnalytics} from 'sentry/utils/analytics'; import type EventView from 'sentry/utils/discover/eventView'; import {SavedQueryDatasets} from 'sentry/utils/discover/types'; import type {UseApiQueryResult} from 'sentry/utils/queryClient'; import type RequestError from 'sentry/utils/requestError/requestError'; import {useLocation} from 'sentry/utils/useLocation'; import {hasDatasetSelector} from 'sentry/views/dashboards/utils'; import {ProjectsRenderer} from 'sentry/views/explore/tables/tracesTable/fieldRenderers'; import {useModuleURLBuilder} from 'sentry/views/insights/common/utils/useModuleURL'; import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters'; import {useTraceStateDispatch} from 'sentry/views/performance/newTraceDetails/traceState/traceStateProvider'; import type {TraceMetaQueryResults} from '../traceApi/useTraceMeta'; import TraceConfigurations from '../traceConfigurations'; import type {TraceTree} from '../traceModels/traceTree'; import {useHasTraceNewUi} from '../useHasTraceNewUi'; import {getTraceViewBreadcrumbs} from './breadcrumbs'; import {Meta} from './meta'; import {Title} from './title'; interface TraceMetadataHeaderProps { metaResults: TraceMetaQueryResults; organization: Organization; rootEventResults: UseApiQueryResult; traceEventView: EventView; traceSlug: string; tree: TraceTree; } function PlaceHolder({organization}: {organization: Organization}) { const {view} = useDomainViewFilters(); const moduleURLBuilder = useModuleURLBuilder(true); const location = useLocation(); return ( ); } const PlaceHolderTitleWrapper = styled('div')` display: flex; flex-direction: column; gap: ${space(0.5)}; `; const PlaceHolderHighlightWrapper = styled('div')` display: flex; align-items: center; gap: ${space(1)}; `; const StyledPlaceholder = styled(Placeholder)<{_height: number; _width: number}>` border-radius: ${p => p.theme.borderRadius}; height: ${p => p._height}px; width: ${p => p._width}px; `; function LegacyTraceMetadataHeader(props: TraceMetadataHeaderProps) { const location = useLocation(); const {view} = useDomainViewFilters(); const moduleURLBuilder = useModuleURLBuilder(true); const trackOpenInDiscover = useCallback(() => { trackAnalytics('performance_views.trace_view.open_in_discover', { organization: props.organization, }); }, [props.organization]); return ( {t('Open in Discover')} ); } export function TraceMetaDataHeader(props: TraceMetadataHeaderProps) { const location = useLocation(); const hasNewTraceViewUi = useHasTraceNewUi(); const {view} = useDomainViewFilters(); const moduleURLBuilder = useModuleURLBuilder(true); const dispatch = useTraceStateDispatch(); const onProjectClick = useCallback( (projectSlug: string) => { dispatch({type: 'set query', query: `project:${projectSlug}`, source: 'external'}); }, [dispatch] ); const projectSlugs = useMemo(() => { return Array.from(props.tree.projects).map(p => p.slug); }, [props.tree]); if (!hasNewTraceViewUi) { return ; } const isLoading = props.metaResults.status === 'pending' || props.rootEventResults.isPending || props.tree.type === 'loading'; if (isLoading) { return ; } return ( <Meta organization={props.organization} rootEventResults={props.rootEventResults} tree={props.tree} meta={props.metaResults.data} /> </HeaderRow> <StyledBreak /> {props.rootEventResults.data ? ( <HeaderRow> <StyledWrapper> <HighlightsIconSummary event={props.rootEventResults.data} /> </StyledWrapper> <ProjectsRendererWrapper> <ProjectsRenderer disableLink onProjectClick={onProjectClick} projectSlugs={projectSlugs} visibleAvatarSize={24} maxVisibleProjects={3} /> </ProjectsRendererWrapper> </HeaderRow> ) : null} </HeaderContent> </HeaderLayout> ); } // We cannot change the cursor of the ProjectBadge component so we need to wrap it in a div const ProjectsRendererWrapper = styled('div')` img { cursor: pointer; } `; const HeaderLayout = styled(Layout.Header)` padding: ${space(2)} ${space(2)} !important; `; const HeaderRow = styled('div')` display: flex; justify-content: space-between; &:not(:first-child) { margin: ${space(1)} 0; } @media (max-width: ${p => p.theme.breakpoints.small}) { flex-direction: column; } `; const HeaderContent = styled('div')` display: flex; flex-direction: column; `; const StyledBreak = styled('hr')` margin: 0; border-color: ${p => p.theme.border}; `; const StyledWrapper = styled('span')` display: flex; align-items: center; & > div { padding: 0; } `;