diff --git a/static/app/routes.tsx b/static/app/routes.tsx index 8ee51a5179c908..39972df2b7edbb 100644 --- a/static/app/routes.tsx +++ b/static/app/routes.tsx @@ -1707,6 +1707,10 @@ function buildRoutes() { )} /> {transactionSummaryRoutes} + import('sentry/views/performance/traceDetails'))} + /> import('sentry/views/insights/pages/backend/backendOverviewPage') )} /> + {transactionSummaryRoutes} + import('sentry/views/performance/traceDetails'))} + /> import('sentry/views/insights/pages/mobile/mobileOverviewPage') )} /> + {transactionSummaryRoutes} + import('sentry/views/performance/traceDetails'))} + /> import('sentry/views/insights/pages/ai/aiOverviewPage'))} /> + {transactionSummaryRoutes} + import('sentry/views/performance/traceDetails'))} + /> - } /> + } + /> diff --git a/static/app/views/insights/pages/frontend/frontendPageHeader.tsx b/static/app/views/insights/pages/frontend/frontendPageHeader.tsx index f2c28aee1b7a06..a620116e9f4b35 100644 --- a/static/app/views/insights/pages/frontend/frontendPageHeader.tsx +++ b/static/app/views/insights/pages/frontend/frontendPageHeader.tsx @@ -1,6 +1,9 @@ import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import useOrganization from 'sentry/utils/useOrganization'; -import {DomainViewHeader} from 'sentry/views/insights/pages/domainViewHeader'; +import { + DomainViewHeader, + type Props as HeaderProps, +} from 'sentry/views/insights/pages/domainViewHeader'; import { FRONTEND_LANDING_SUB_PATH, FRONTEND_LANDING_TITLE, @@ -9,12 +12,23 @@ import {DOMAIN_VIEW_BASE_URL} from 'sentry/views/insights/pages/settings'; import {ModuleName} from 'sentry/views/insights/types'; type Props = { - headerActions?: React.ReactNode; - module?: ModuleName; + breadcrumbs?: HeaderProps['additionalBreadCrumbs']; + headerActions?: HeaderProps['additonalHeaderActions']; + headerTitle?: HeaderProps['headerTitle']; + hideDefaultTabs?: HeaderProps['hideDefaultTabs']; + module?: HeaderProps['selectedModule']; + tabs?: HeaderProps['tabs']; }; // TODO - add props to append to breadcrumbs and change title -export function FrontendHeader({module, headerActions}: Props) { +export function FrontendHeader({ + module, + headerActions, + breadcrumbs, + headerTitle, + hideDefaultTabs, + tabs, +}: Props) { const {slug} = useOrganization(); const frontendBaseUrl = normalizeUrl( @@ -28,9 +42,12 @@ export function FrontendHeader({module, headerActions}: Props) { domainBaseUrl={frontendBaseUrl} domainTitle={FRONTEND_LANDING_TITLE} modules={modules} + tabs={tabs} selectedModule={module} additonalHeaderActions={headerActions} - headerTitle={FRONTEND_LANDING_TITLE} + hideDefaultTabs={hideDefaultTabs} + additionalBreadCrumbs={breadcrumbs} + headerTitle={headerTitle || FRONTEND_LANDING_TITLE} /> ); } diff --git a/static/app/views/insights/pages/useDomainViewUrl.ts b/static/app/views/insights/pages/useDomainViewUrl.ts deleted file mode 100644 index 3dd218178e1a39..00000000000000 --- a/static/app/views/insights/pages/useDomainViewUrl.ts +++ /dev/null @@ -1,34 +0,0 @@ -export function useModuleURLBuilder( - bare: boolean = false, - autoDetectDomainView: boolean = true, - forceDomainView?: boolean // TODO - eventually this param will be removed once we don't have modules in two spots -): URLBuilder { - const organization = useOrganization({allowNull: true}); // Some parts of the app, like the main sidebar, render even if the organization isn't available (during loading, or at all). - const {isInDomainView, view: currentView} = useDomainViewFilters(); - - if (!organization) { - // If there isn't an organization, items that link to modules won't be visible, so this is a fallback just-in-case, and isn't trying too hard to be useful - return () => ''; - } - - const {slug} = organization; - - if ((autoDetectDomainView && isInDomainView) || forceDomainView) { - return function (moduleName: RoutableModuleNames, domainView?: DomainView) { - const view = domainView ?? currentView; - return bare - ? `${DOMAIN_VIEW_BASE_URL}/${view}/${MODULE_BASE_URLS[moduleName]}` - : normalizeUrl( - `/organizations/${slug}/${DOMAIN_VIEW_BASE_URL}/${view}/${MODULE_BASE_URLS[moduleName]}` - ); - }; - } - - return function (moduleName: RoutableModuleNames) { - return bare - ? `${INSIGHTS_BASE_URL}/${MODULE_BASE_URLS[moduleName]}` - : normalizeUrl( - `/organizations/${slug}/${INSIGHTS_BASE_URL}/${MODULE_BASE_URLS[moduleName]}` - ); - }; -} diff --git a/static/app/views/performance/traceDetails/utils.tsx b/static/app/views/performance/traceDetails/utils.tsx index 07261a89301bf2..d6260cc105382d 100644 --- a/static/app/views/performance/traceDetails/utils.tsx +++ b/static/app/views/performance/traceDetails/utils.tsx @@ -12,6 +12,8 @@ import type { } from 'sentry/utils/performance/quickTrace/types'; import {isTraceSplitResult, reduceTrace} from 'sentry/utils/performance/quickTrace/utils'; import normalizeUrl from 'sentry/utils/url/normalizeUrl'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getPerformanceBaseUrl} from 'sentry/views/performance/utils'; import {DEFAULT_TRACE_ROWS_LIMIT} from './limitExceededMessage'; import type {TraceInfo} from './types'; @@ -27,6 +29,7 @@ export function getTraceDetailsUrl({ demo, location, source, + view, }: { dateSelection; location: Location; @@ -40,6 +43,7 @@ export function getTraceDetailsUrl({ // to trace view are updated to use spand ids of transactions instead of event ids. targetId?: string; timestamp?: string | number; + view?: DomainView; }): LocationDescriptorObject { const {start, end, statsPeriod} = dateSelection; @@ -50,10 +54,10 @@ export function getTraceDetailsUrl({ [PAGE_URL_PARAM.PAGE_END]: end, }; + const performanceBaseUrl = getPerformanceBaseUrl(organization.slug, view); + const oldTraceUrl = { - pathname: normalizeUrl( - `/organizations/${organization.slug}/performance/trace/${traceSlug}/` - ), + pathname: normalizeUrl(`${performanceBaseUrl}/trace/${traceSlug}/`), query: queryParams, }; @@ -66,9 +70,7 @@ export function getTraceDetailsUrl({ queryParams.node = [`span-${spanId}`, `txn-${targetId ?? eventId}`]; } return { - pathname: normalizeUrl( - `/organizations/${organization.slug}/performance/trace/${traceSlug}/` - ), + pathname: normalizeUrl(`${performanceBaseUrl}/trace/${traceSlug}/`), query: { ...queryParams, timestamp: getTimeStampFromTableDateField(timestamp), diff --git a/static/app/views/performance/transactionSummary/aggregateSpanWaterfall/utils.tsx b/static/app/views/performance/transactionSummary/aggregateSpanWaterfall/utils.tsx index 3985f30603145a..d9cefb22717186 100644 --- a/static/app/views/performance/transactionSummary/aggregateSpanWaterfall/utils.tsx +++ b/static/app/views/performance/transactionSummary/aggregateSpanWaterfall/utils.tsx @@ -2,19 +2,23 @@ import type {Query} from 'history'; import {decodeScalar} from 'sentry/utils/queryString'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; export function aggregateWaterfallRouteWithQuery({ orgSlug, transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { - const pathname = `/organizations/${orgSlug}/performance/summary/aggregateWaterfall/`; + const pathname = `${getTransactionSummaryBaseUrl(orgSlug, view)}/aggregateWaterfall/`; const filter = decodeScalar(query.query); let httpMethod: string | undefined = undefined; diff --git a/static/app/views/performance/transactionSummary/header.tsx b/static/app/views/performance/transactionSummary/header.tsx index 502576e81e5fce..a513fef35248c9 100644 --- a/static/app/views/performance/transactionSummary/header.tsx +++ b/static/app/views/performance/transactionSummary/header.tsx @@ -1,4 +1,4 @@ -import {useCallback} from 'react'; +import {Fragment, useCallback} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; @@ -23,7 +23,28 @@ import HasMeasurementsQuery from 'sentry/utils/performance/vitals/hasMeasurement import {isProfilingSupportedOrProjectHasProfiles} from 'sentry/utils/profiling/platforms'; import useReplayCountForTransactions from 'sentry/utils/replayCount/useReplayCountForTransactions'; import projectSupportsReplay from 'sentry/utils/replays/projectSupportsReplay'; +import normalizeUrl from 'sentry/utils/url/normalizeUrl'; +import {useNavigate} from 'sentry/utils/useNavigate'; +import {AiHeader} from 'sentry/views/insights/pages/ai/aiPageHeader'; +import {AI_LANDING_SUB_PATH} from 'sentry/views/insights/pages/ai/settings'; +import {BackendHeader} from 'sentry/views/insights/pages/backend/backendPageHeader'; +import {BACKEND_LANDING_SUB_PATH} from 'sentry/views/insights/pages/backend/settings'; +import {FrontendHeader} from 'sentry/views/insights/pages/frontend/frontendPageHeader'; +import {FRONTEND_LANDING_SUB_PATH} from 'sentry/views/insights/pages/frontend/settings'; +import {MobileHeader} from 'sentry/views/insights/pages/mobile/mobilePageHeader'; +import {MOBILE_LANDING_SUB_PATH} from 'sentry/views/insights/pages/mobile/settings'; +import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters'; import Breadcrumb from 'sentry/views/performance/breadcrumb'; +import {aggregateWaterfallRouteWithQuery} from 'sentry/views/performance/transactionSummary/aggregateSpanWaterfall/utils'; +import {TAB_ANALYTICS} from 'sentry/views/performance/transactionSummary/pageLayout'; +import {anomaliesRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionAnomalies/utils'; +import {eventsRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionEvents/utils'; +import {profilesRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionProfiles/utils'; +import {replaysRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionReplays/utils'; +import {spansRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionSpans/utils'; +import {tagsRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionTags/utils'; +import {transactionSummaryRouteWithQuery} from 'sentry/views/performance/transactionSummary/utils'; +import {getSelectedProjectPlatforms} from 'sentry/views/performance/utils'; import {getCurrentLandingDisplay, LandingDisplayField} from '../landing/utils'; @@ -57,6 +78,67 @@ function TransactionHeader({ currentTab, hasWebVitals, }: Props) { + const {isInDomainView, view} = useDomainViewFilters(); + const navigate = useNavigate(); + + const getNewRoute = useCallback( + (newTab: Tab) => { + if (!transactionName) { + return {}; + } + + const routeQuery = { + orgSlug: organization.slug, + transaction: transactionName, + projectID: projectId, + query: location.query, + view, + }; + + switch (newTab) { + case Tab.TAGS: + return tagsRouteWithQuery(routeQuery); + case Tab.EVENTS: + return eventsRouteWithQuery(routeQuery); + case Tab.SPANS: + return spansRouteWithQuery(routeQuery); + case Tab.ANOMALIES: + return anomaliesRouteWithQuery(routeQuery); + case Tab.REPLAYS: + return replaysRouteWithQuery(routeQuery); + case Tab.PROFILING: { + return profilesRouteWithQuery(routeQuery); + } + case Tab.AGGREGATE_WATERFALL: + return aggregateWaterfallRouteWithQuery(routeQuery); + case Tab.TRANSACTION_SUMMARY: + default: + return transactionSummaryRouteWithQuery(routeQuery); + } + }, + [location.query, organization.slug, projectId, transactionName, view] + ); + + const onTabChange = useCallback( + (newTab: string) => { + // Prevent infinite rerenders + if (newTab === currentTab) { + return; + } + + const analyticsKey = TAB_ANALYTICS[newTab]; + if (analyticsKey) { + trackAnalytics(analyticsKey, { + organization, + project_platforms: getSelectedProjectPlatforms(location, projects), + }); + } + + navigate(normalizeUrl(getNewRoute(newTab as Tab))); + }, + [getNewRoute, organization, location, projects, currentTab, navigate] + ); + function handleCreateAlertSuccess() { trackAnalytics('performance_views.summary.create_alert_clicked', { organization, @@ -117,6 +199,109 @@ function TransactionHeader({ }); const replaysCount = getReplayCountForTransaction(transactionName); + const tabList = ( + + {({hasMeasurements}) => { + const renderWebVitals = getWebVitals(!!hasMeasurements); + + return ( + + {t('Overview')} + {t('Sampled Events')} + {t('Tags')} + {t('Spans')} + + + + + + + ); + }} + + ); + + if (isInDomainView) { + const headerProps = { + headerTitle: ( + + {project && ( + + )} + + {transactionName} + + + ), + hideDefaultTabs: true, + tabs: { + onTabChange, + tabList, + value: currentTab, + }, + }; + if (view === FRONTEND_LANDING_SUB_PATH) { + return ; + } + if (view === BACKEND_LANDING_SUB_PATH) { + return ; + } + if (view === AI_LANDING_SUB_PATH) { + return ; + } + if (view === MOBILE_LANDING_SUB_PATH) { + return ; + } + } + return ( diff --git a/static/app/views/performance/transactionSummary/pageLayout.tsx b/static/app/views/performance/transactionSummary/pageLayout.tsx index 12d9a591f72abe..c036840b167889 100644 --- a/static/app/views/performance/transactionSummary/pageLayout.tsx +++ b/static/app/views/performance/transactionSummary/pageLayout.tsx @@ -49,7 +49,7 @@ type TabEvents = | 'performance_views.events.events_tab_clicked' | 'performance_views.spans.spans_tab_clicked'; -const TAB_ANALYTICS: Partial> = { +export const TAB_ANALYTICS: Partial> = { [Tab.WEB_VITALS]: 'performance_views.vitals.vitals_tab_clicked', [Tab.TAGS]: 'performance_views.tags.tags_tab_clicked', [Tab.EVENTS]: 'performance_views.events.events_tab_clicked', diff --git a/static/app/views/performance/transactionSummary/transactionAnomalies/utils.tsx b/static/app/views/performance/transactionSummary/transactionAnomalies/utils.tsx index f8b6db1bea4a3e..2e0ca9e237d56b 100644 --- a/static/app/views/performance/transactionSummary/transactionAnomalies/utils.tsx +++ b/static/app/views/performance/transactionSummary/transactionAnomalies/utils.tsx @@ -5,9 +5,17 @@ import EventView from 'sentry/utils/discover/eventView'; import type {AnomalyConfidence} from 'sentry/utils/performance/anomalies/anomaliesQuery'; import {decodeScalar} from 'sentry/utils/queryString'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; -export function generateAnomaliesRoute({orgSlug}: {orgSlug: string}): string { - return `/organizations/${orgSlug}/performance/summary/anomalies/`; +export function generateAnomaliesRoute({ + orgSlug, + view, +}: { + orgSlug: string; + view?: DomainView; +}): string { + return `${getTransactionSummaryBaseUrl(orgSlug, view)}/anomalies/`; } export const ANOMALY_FLAG = 'performance-anomaly-detection-ui'; @@ -17,14 +25,17 @@ export function anomaliesRouteWithQuery({ transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { const pathname = generateAnomaliesRoute({ orgSlug, + view, }); return { diff --git a/static/app/views/performance/transactionSummary/transactionEvents/content.tsx b/static/app/views/performance/transactionSummary/transactionEvents/content.tsx index 94f25268f6c655..c9ba9611e58448 100644 --- a/static/app/views/performance/transactionSummary/transactionEvents/content.tsx +++ b/static/app/views/performance/transactionSummary/transactionEvents/content.tsx @@ -25,6 +25,7 @@ import {decodeScalar} from 'sentry/utils/queryString'; import projectSupportsReplay from 'sentry/utils/replays/projectSupportsReplay'; import {useRoutes} from 'sentry/utils/useRoutes'; import {hasDatasetSelector} from 'sentry/views/dashboards/utils'; +import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters'; import { platformToPerformanceType, ProjectPerformanceType, @@ -76,6 +77,7 @@ function EventsContent(props: Props) { projects, } = props; const routes = useRoutes(); + const domainViewFilters = useDomainViewFilters(); const {eventView, titles} = useMemo(() => { const eventViewClone = originalEventView.clone(); @@ -166,6 +168,7 @@ function EventsContent(props: Props) { setError={setError} columnTitles={titles} transactionName={transactionName} + domainViewFilters={domainViewFilters} /> ); diff --git a/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx b/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx index ef4cdf1fce59c4..c234199faae5b4 100644 --- a/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx +++ b/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx @@ -38,6 +38,7 @@ import parseLinkHeader from 'sentry/utils/parseLinkHeader'; import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry'; import CellAction, {Actions, updateQuery} from 'sentry/views/discover/table/cellAction'; import type {TableColumn} from 'sentry/views/discover/table/types'; +import type {DomainViewFilters} from 'sentry/views/insights/pages/useFilters'; import {COLUMN_TITLES} from '../../data'; import {TraceViewSources} from '../../newTraceDetails/traceMetadataHeader'; @@ -93,6 +94,7 @@ type Props = { transactionName: string; columnTitles?: string[]; customColumns?: ('attachments' | 'minidump')[]; + domainViewFilters?: DomainViewFilters; excludedTags?: string[]; hidePagination?: boolean; isEventLoading?: boolean; @@ -206,6 +208,7 @@ class EventsTable extends Component { organization, transactionName: transactionName, source: TraceViewSources.PERFORMANCE_TRANSACTION_SUMMARY, + view: this.props.domainViewFilters?.view, }); } else { target = generateTraceLink(transactionName)( diff --git a/static/app/views/performance/transactionSummary/transactionEvents/utils.tsx b/static/app/views/performance/transactionSummary/transactionEvents/utils.tsx index 5c1dc9854f90cb..b1662018969920 100644 --- a/static/app/views/performance/transactionSummary/transactionEvents/utils.tsx +++ b/static/app/views/performance/transactionSummary/transactionEvents/utils.tsx @@ -5,10 +5,11 @@ import type {TableDataRow} from 'sentry/utils/discover/discoverQuery'; import type EventView from 'sentry/utils/discover/eventView'; import type {QueryFieldValue} from 'sentry/utils/discover/fields'; import {decodeScalar} from 'sentry/utils/queryString'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; import type {SpanOperationBreakdownFilter} from '../filter'; import {filterToField} from '../filter'; -import {TransactionFilterOptions} from '../utils'; +import {getTransactionSummaryBaseUrl, TransactionFilterOptions} from '../utils'; export enum EventsDisplayFilterName { P50 = 'p50', @@ -91,13 +92,15 @@ export function eventsRouteWithQuery({ transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { - const pathname = `/organizations/${orgSlug}/performance/summary/events/`; + const pathname = `${getTransactionSummaryBaseUrl(orgSlug, view)}/events/`; return { pathname, query: { diff --git a/static/app/views/performance/transactionSummary/transactionProfiles/utils.ts b/static/app/views/performance/transactionSummary/transactionProfiles/utils.ts index 51b461d69fd8bc..84ad178971f75b 100644 --- a/static/app/views/performance/transactionSummary/transactionProfiles/utils.ts +++ b/static/app/views/performance/transactionSummary/transactionProfiles/utils.ts @@ -1,17 +1,22 @@ import type {Query} from 'history'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; + export function profilesRouteWithQuery({ orgSlug, transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { - const pathname = `/organizations/${orgSlug}/performance/summary/profiles/`; + const pathname = `${getTransactionSummaryBaseUrl(orgSlug, view)}/profiles/`; return { pathname, diff --git a/static/app/views/performance/transactionSummary/transactionReplays/utils.ts b/static/app/views/performance/transactionSummary/transactionReplays/utils.ts index 372013e305e0c7..6eb7c0365b70f1 100644 --- a/static/app/views/performance/transactionSummary/transactionReplays/utils.ts +++ b/static/app/views/performance/transactionSummary/transactionReplays/utils.ts @@ -1,17 +1,22 @@ import type {Query} from 'history'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; + export function replaysRouteWithQuery({ orgSlug, transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { - const pathname = `/organizations/${orgSlug}/performance/summary/replays/`; + const pathname = `${getTransactionSummaryBaseUrl(orgSlug, view)}/replays/`; return { pathname, diff --git a/static/app/views/performance/transactionSummary/transactionSpans/spanDetails/utils.tsx b/static/app/views/performance/transactionSummary/transactionSpans/spanDetails/utils.tsx index 548438d8d39546..5fde4db2c42eca 100644 --- a/static/app/views/performance/transactionSummary/transactionSpans/spanDetails/utils.tsx +++ b/static/app/views/performance/transactionSummary/transactionSpans/spanDetails/utils.tsx @@ -1,16 +1,20 @@ import type {Query} from 'history'; import type {SpanSlug} from 'sentry/utils/performance/suspectSpans/types'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; function generateSpanDetailsRoute({ orgSlug, spanSlug, + view, }: { orgSlug: string; spanSlug: SpanSlug; + view?: DomainView; }): string { const spanComponent = `${encodeURIComponent(spanSlug.op)}:${spanSlug.group}`; - return `/organizations/${orgSlug}/performance/summary/spans/${spanComponent}/`; + return `${getTransactionSummaryBaseUrl(orgSlug, view)}/spans/${spanComponent}/`; } export function spanDetailsRouteWithQuery({ diff --git a/static/app/views/performance/transactionSummary/transactionSpans/utils.tsx b/static/app/views/performance/transactionSummary/transactionSpans/utils.tsx index 4e9f0c9160ab2c..c1cde655b3f1d6 100644 --- a/static/app/views/performance/transactionSummary/transactionSpans/utils.tsx +++ b/static/app/views/performance/transactionSummary/transactionSpans/utils.tsx @@ -9,12 +9,20 @@ import {isAggregateField} from 'sentry/utils/discover/fields'; import type {SpanSlug} from 'sentry/utils/performance/suspectSpans/types'; import {decodeScalar} from 'sentry/utils/queryString'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; import type {SpanSort, SpanSortOption} from './types'; import {SpanSortOthers, SpanSortPercentiles} from './types'; -export function generateSpansRoute({orgSlug}: {orgSlug: string}): string { - return `/organizations/${orgSlug}/performance/summary/spans/`; +export function generateSpansRoute({ + orgSlug, + view, +}: { + orgSlug: string; + view?: DomainView; +}): string { + return `${getTransactionSummaryBaseUrl(orgSlug, view)}/spans/`; } export function spansRouteWithQuery({ @@ -22,14 +30,17 @@ export function spansRouteWithQuery({ transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { const pathname = generateSpansRoute({ orgSlug, + view, }); return { diff --git a/static/app/views/performance/transactionSummary/transactionTags/utils.tsx b/static/app/views/performance/transactionSummary/transactionTags/utils.tsx index 47b4ced70e3aa6..843e1988da0c6f 100644 --- a/static/app/views/performance/transactionSummary/transactionTags/utils.tsx +++ b/static/app/views/performance/transactionSummary/transactionTags/utils.tsx @@ -3,9 +3,17 @@ import type {Location, Query} from 'history'; import type {Organization} from 'sentry/types/organization'; import {trackAnalytics} from 'sentry/utils/analytics'; import {decodeScalar} from 'sentry/utils/queryString'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; +import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils'; -export function generateTagsRoute({orgSlug}: {orgSlug: string}): string { - return `/organizations/${orgSlug}/performance/summary/tags/`; +export function generateTagsRoute({ + orgSlug, + view, +}: { + orgSlug: string; + view?: DomainView; +}): string { + return `${getTransactionSummaryBaseUrl(orgSlug, view)}/tags/`; } export function decodeSelectedTagKey(location: Location): string | undefined { @@ -21,14 +29,17 @@ export function tagsRouteWithQuery({ transaction, projectID, query, + view, }: { orgSlug: string; query: Query; transaction: string; projectID?: string | string[]; + view?: DomainView; }) { const pathname = generateTagsRoute({ orgSlug, + view, }); return { diff --git a/static/app/views/performance/transactionSummary/utils.tsx b/static/app/views/performance/transactionSummary/utils.tsx index 8d434e12632e20..af84741536f8a0 100644 --- a/static/app/views/performance/transactionSummary/utils.tsx +++ b/static/app/views/performance/transactionSummary/utils.tsx @@ -16,6 +16,7 @@ import {MutableSearch} from 'sentry/utils/tokenizeSearch'; import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import type {DomainView} from 'sentry/views/insights/pages/useFilters'; import {getTraceDetailsUrl} from 'sentry/views/performance/traceDetails/utils'; +import {getPerformanceBaseUrl} from 'sentry/views/performance/utils'; import {TraceViewSources} from '../newTraceDetails/traceMetadataHeader'; @@ -45,7 +46,9 @@ export function generateTransactionSummaryRoute({ view?: DomainView; // TODO - this should be mantatory once we release domain view }): string { if (view) { - return `/organizations/${orgSlug}/performance/${view}/summary/${subPath ? `${subPath}/` : ''}`; + return normalizeUrl( + `/organizations/${orgSlug}/performance/${view}/summary/${subPath ? `${subPath}/` : ''}` + ); } return `/organizations/${orgSlug}/performance/summary/${subPath ? `${subPath}/` : ''}`; } @@ -264,6 +267,11 @@ export function generateReplayLink(routes: PlainRoute[]) { }; }; } + +export function getTransactionSummaryBaseUrl(orgSlug: string, view?: DomainView) { + return `${getPerformanceBaseUrl(orgSlug, view)}/summary`; +} + export const SidebarSpacer = styled('div')` margin-top: ${space(3)}; `; diff --git a/static/app/views/performance/utils/index.tsx b/static/app/views/performance/utils/index.tsx index 9e29928014f01c..b15779b068fab0 100644 --- a/static/app/views/performance/utils/index.tsx +++ b/static/app/views/performance/utils/index.tsx @@ -27,6 +27,7 @@ import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; import {hasDatasetSelector} from 'sentry/views/dashboards/utils'; +import type {DomainView} from 'sentry/views/insights/pages/useFilters'; import {DEFAULT_MAX_DURATION} from '../trends/utils'; @@ -391,3 +392,10 @@ export function usePerformanceGeneralProjectSettings(projectId?: number) { } ); } + +export function getPerformanceBaseUrl(orgSlug: string, view?: DomainView) { + if (view) { + return normalizeUrl(`/organizations/${orgSlug}/performance/${view}`); + } + return normalizeUrl(`/organizations/${orgSlug}/performance`); +}