diff --git a/static/app/components/profiling/continuousProfileHeader.tsx b/static/app/components/profiling/continuousProfileHeader.tsx index f675bc1a9b8b5d..63039c9ac0bb2d 100644 --- a/static/app/components/profiling/continuousProfileHeader.tsx +++ b/static/app/components/profiling/continuousProfileHeader.tsx @@ -1,4 +1,4 @@ -import {useCallback, useMemo} from 'react'; +import {Fragment, useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import {LinkButton} from '@sentry/scraps/button'; @@ -53,20 +53,33 @@ export function ContinuousProfileHeader({transaction}: ContinuousProfileHeader) - - {hasPageFrameFeature ? ( + {hasPageFrameFeature ? ( + + {transactionTarget && ( + + + {t('Go to Trace')} + + + )} {null} - ) : ( + + ) : ( + - )} - {transactionTarget && ( - - {t('Go to Trace')} - - )} - + {transactionTarget && ( + + {t('Go to Trace')} + + )} + + )} ); } diff --git a/static/app/components/profiling/profileHeader.tsx b/static/app/components/profiling/profileHeader.tsx index 270561785dfbf3..6fd8134888fce6 100644 --- a/static/app/components/profiling/profileHeader.tsx +++ b/static/app/components/profiling/profileHeader.tsx @@ -1,4 +1,4 @@ -import {useCallback, useMemo} from 'react'; +import {Fragment, useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import {LinkButton} from '@sentry/scraps/button'; @@ -91,20 +91,33 @@ function ProfileHeader({transaction, projectId, eventId}: ProfileHeaderProps) { - - {hasPageFrameFeature ? ( + {hasPageFrameFeature ? ( + + {transactionTarget && ( + + + {t('Go to Trace')} + + + )} {null} - ) : ( + + ) : ( + - )} - {transactionTarget && ( - - {t('Go to Trace')} - - )} - + {transactionTarget && ( + + {t('Go to Trace')} + + )} + + )} ); } diff --git a/static/app/components/workflowEngine/layout/list.tsx b/static/app/components/workflowEngine/layout/list.tsx index 428ff70ff34e63..936ac954010979 100644 --- a/static/app/components/workflowEngine/layout/list.tsx +++ b/static/app/components/workflowEngine/layout/list.tsx @@ -4,6 +4,8 @@ import * as Layout from 'sentry/components/layouts/thirds'; import {NoProjectMessage} from 'sentry/components/noProjectMessage'; import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip'; import {useOrganization} from 'sentry/utils/useOrganization'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; interface WorkflowEngineListLayoutProps { actions: React.ReactNode; @@ -26,6 +28,7 @@ export function WorkflowEngineListLayout({ docsUrl, }: WorkflowEngineListLayoutProps) { const organization = useOrganization(); + const hasPageFrameFeature = useHasPageFrameFeature(); return ( @@ -37,7 +40,11 @@ export function WorkflowEngineListLayout({ - {actions} + {hasPageFrameFeature ? ( + {actions} + ) : ( + {actions} + )} diff --git a/static/app/views/alerts/list/header.tsx b/static/app/views/alerts/list/header.tsx index 8575b98953b047..66f9e277ba4bb7 100644 --- a/static/app/views/alerts/list/header.tsx +++ b/static/app/views/alerts/list/header.tsx @@ -1,3 +1,5 @@ +import {Fragment} from 'react'; + import {LinkButton} from '@sentry/scraps/button'; import {Grid} from '@sentry/scraps/layout'; import {TabList} from '@sentry/scraps/tabs'; @@ -66,38 +68,63 @@ export function AlertHeader({activeTab}: Props) { /> - - - - {t('Create Alert')} - - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + + {t('Create Alert')} + + } + aria-label={t('Settings')} + /> + + + {null} + + + ) : ( + + + + {t('Create Alert')} + - )} - } - aria-label={t('Settings')} - /> - - + } + aria-label={t('Settings')} + /> + + + )} {alertRulesLink} diff --git a/static/app/views/alerts/rules/issue/details/ruleDetails.tsx b/static/app/views/alerts/rules/issue/details/ruleDetails.tsx index 47b2eede8bfbc1..c3cd309cb5fa45 100644 --- a/static/app/views/alerts/rules/issue/details/ruleDetails.tsx +++ b/static/app/views/alerts/rules/issue/details/ruleDetails.tsx @@ -45,6 +45,8 @@ import {APIUsageWarningBanner} from 'sentry/views/alerts/rules/APIUsageWarningBa import {findIncompatibleRules} from 'sentry/views/alerts/rules/issue'; import {ALERT_DEFAULT_CHART_PERIOD} from 'sentry/views/alerts/rules/metric/details/constants'; import {UserSnoozeDeprecationBanner} from 'sentry/views/alerts/rules/userSnoozeDeprecationBanner'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {IssueAlertDetailsChart} from './alertChart'; import {AlertRuleIssuesList} from './issuesList'; @@ -78,6 +80,7 @@ const getIssueAlertDetailsQueryKey = ({ ]; export default function AlertRuleDetails() { + const hasPageFrameFeature = useHasPageFrameFeature(); const queryClient = useQueryClient(); const organization = useOrganization(); const api = useApi(); @@ -422,8 +425,8 @@ export default function AlertRuleDetails() { {rule.name} - - + {hasPageFrameFeature ? ( + {({hasAccess}) => ( {rule.status === 'disabled' ? t('Edit to enable') : t('Edit Rule')} - - + + ) : ( + + + + {({hasAccess}) => ( + + )} + + } + to={duplicateLink} + disabled={rule.status === 'disabled'} + > + {t('Duplicate')} + + } + to={makeAlertsPathname({ + path: `/rules/${projectSlug}/${ruleId}/`, + organization, + })} + onClick={() => + trackAnalytics('issue_alert_rule_details.edit_clicked', { + organization, + rule_id: parseInt(ruleId, 10), + }) + } + > + {rule.status === 'disabled' ? t('Edit to enable') : t('Edit Rule')} + + + + )} diff --git a/static/app/views/alerts/rules/metric/details/header.tsx b/static/app/views/alerts/rules/metric/details/header.tsx index 33136f361d40b3..90f413fa5634ad 100644 --- a/static/app/views/alerts/rules/metric/details/header.tsx +++ b/static/app/views/alerts/rules/metric/details/header.tsx @@ -24,6 +24,8 @@ import { deprecateTransactionAlerts, hasEAPAlerts, } from 'sentry/views/insights/common/utils/hasEAPAlerts'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; type Props = { hasMetricRuleDetailsError: boolean; @@ -44,6 +46,7 @@ export function DetailsHeader({ project, onSnooze, }: Props) { + const hasPageFrameFeature = useHasPageFrameFeature(); const isRuleReady = !!rule && !hasMetricRuleDetailsError; const ruleTitle = rule && !hasMetricRuleDetailsError ? rule.name : ''; const settingsLink = rule @@ -111,8 +114,8 @@ export function DetailsHeader({ {ruleTitle} - - + {hasPageFrameFeature ? ( + {rule && project && ( {({hasAccess}) => ( @@ -147,8 +150,47 @@ export function DetailsHeader({ } to={settingsLink}> {t('Edit Rule')} - - + + ) : ( + + + {rule && project && ( + + {({hasAccess}) => ( + + )} + + )} + } + to={duplicateLink} + disabled={deprecateTransactionsAlerts} + tooltipProps={{ + title: deprecateTransactionsAlerts + ? hasEAPAlerts(organization) + ? t( + 'Transaction alerts are being deprecated. Please create Span alerts instead.' + ) + : t('Transaction alerts are being deprecated.') + : undefined, + }} + > + {t('Duplicate')} + + } to={settingsLink}> + {t('Edit Rule')} + + + + )} ); } diff --git a/static/app/views/alerts/rules/uptime/details.tsx b/static/app/views/alerts/rules/uptime/details.tsx index c96e533999faca..d09e3690d8f5bb 100644 --- a/static/app/views/alerts/rules/uptime/details.tsx +++ b/static/app/views/alerts/rules/uptime/details.tsx @@ -31,6 +31,8 @@ import { useDetectorQuery, } from 'sentry/views/detectors/hooks'; import {useUptimeMonitorSummaries} from 'sentry/views/insights/uptime/utils/useUptimeMonitorSummary'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {UptimeDetailsSidebar} from './detailsSidebar'; import {DetailsTimeline} from './detailsTimeline'; @@ -40,6 +42,7 @@ import {UptimeChecksTable} from './uptimeChecksTable'; import {UptimeIssues} from './uptimeIssues'; export default function UptimeAlertDetails() { + const hasPageFrameFeature = useHasPageFrameFeature(); const {detectorId, projectId} = useParams<{detectorId: string; projectId: string}>(); const api = useApi(); @@ -149,12 +152,12 @@ export default function UptimeAlertDetails() { {detector.name} - - + {hasPageFrameFeature ? ( + toggleStatus(data)} - size="sm" disabled={!canEdit} {...(canEdit ? {} : {tooltipProps: {title: permissionTooltipText}})} /> @@ -170,8 +173,32 @@ export default function UptimeAlertDetails() { > {t('Edit Rule')} - - + + ) : ( + + + toggleStatus(data)} + size="sm" + disabled={!canEdit} + {...(canEdit ? {} : {tooltipProps: {title: permissionTooltipText}})} + /> + } + disabled={!canEdit} + tooltipProps={{title: canEdit ? undefined : permissionTooltipText}} + to={makeAlertsPathname({ + path: `/uptime-rules/${project.slug}/${detectorId}/`, + organization, + })} + > + {t('Edit Rule')} + + + + )} diff --git a/static/app/views/dashboards/detail.tsx b/static/app/views/dashboards/detail.tsx index 47aed9a222d246..70b78b042ea79b 100644 --- a/static/app/views/dashboards/detail.tsx +++ b/static/app/views/dashboards/detail.tsx @@ -79,6 +79,8 @@ import { import {convertWidgetToQueryParams} from 'sentry/views/dashboards/widgetBuilder/utils/convertWidgetToBuilderStateParams'; import {getDefaultWidget} from 'sentry/views/dashboards/widgetBuilder/utils/getDefaultWidget'; import {getTopNConvertedDefaultWidgets} from 'sentry/views/dashboards/widgetLibrary/data'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {generatePerformanceEventView} from 'sentry/views/performance/data'; import {MetricsDataSwitcher} from 'sentry/views/performance/landing/metricsDataSwitcher'; import {MetricsDataSwitcherAlert} from 'sentry/views/performance/landing/metricsDataSwitcherAlert'; @@ -147,6 +149,7 @@ type Props = { router: InjectedRouter; theme: Theme; children?: React.ReactNode; + hasPageFrameFeature?: boolean; onDashboardUpdate?: (updatedDashboard: DashboardDetails) => void; storageNamespace?: string; widgetInterval?: string; @@ -1154,23 +1157,43 @@ class DashboardDetail extends Component { /> - - - + {this.props.hasPageFrameFeature ? ( + + + + ) : ( + + + + )} )} @@ -1464,6 +1487,7 @@ export function DashboardDetailWithInjectedProps( const router = useRouter(); const [chartInterval] = useChartInterval(); const queryClient = useQueryClient(); + const hasPageFrameFeature = useHasPageFrameFeature(); // Always use the validated chart interval so the UI dropdown and widget // requests stay in sync. chartInterval is validated against the current page // filter period (e.g. won't return 1m for a 30d range) and always has a value. @@ -1484,6 +1508,7 @@ export function DashboardDetailWithInjectedProps( router={router} widgetInterval={widgetInterval} queryClient={queryClient} + hasPageFrameFeature={hasPageFrameFeature} /> ); } diff --git a/static/app/views/dashboards/manage/index.tsx b/static/app/views/dashboards/manage/index.tsx index e64809dd3e8ec0..9530d76c8b13b7 100644 --- a/static/app/views/dashboards/manage/index.tsx +++ b/static/app/views/dashboards/manage/index.tsx @@ -1,4 +1,4 @@ -import {useEffect, useMemo, useRef, useState} from 'react'; +import {Fragment, useEffect, useMemo, useRef, useState} from 'react'; import styled from '@emotion/styled'; import * as Sentry from '@sentry/react'; import type {Query} from 'history'; @@ -635,121 +635,228 @@ function ManageDashboards() { /> - - - {!hasPrebuiltDashboards && ( - - {t('Show Templates')} - - - )} - - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + {!hasPrebuiltDashboards && ( + + {t('Show Templates')} + + + )} + + {({hasFeature: hasAiGenerate}) => + hasAiGenerate && areAiFeaturesAllowed ? ( + + {({ + hasReachedDashboardLimit, + isLoading: isLoadingDashboardsLimit, + limitMessage, + }) => ( + onCreate(), + disabled: + hasReachedDashboardLimit || + isLoadingDashboardsLimit, + details: limitMessage, + }, + { + key: 'create-dashboard-agent', + textValue: t('Generate dashboard'), + label: ( + + {t('Generate dashboard')} + + + ), + onAction: () => onGenerateDashboard(), + }, + ]} + trigger={triggerProps => ( + + )} + /> + )} + + ) : ( + + {({ + hasReachedDashboardLimit, + isLoading: isLoadingDashboardsLimit, + limitMessage, + }) => ( + + )} + + ) + } + + + + + + + {null} + + + ) : ( + + + {!hasPrebuiltDashboards && ( + + {t('Show Templates')} + + + )} + - )} - - {({hasFeature: hasAiGenerate}) => - hasAiGenerate && areAiFeaturesAllowed ? ( - - {({ - hasReachedDashboardLimit, - isLoading: isLoadingDashboardsLimit, - limitMessage, - }) => ( - onCreate(), - disabled: - hasReachedDashboardLimit || - isLoadingDashboardsLimit, - details: limitMessage, - }, - { - key: 'create-dashboard-agent', - textValue: t('Generate dashboard'), - label: ( - - {t('Generate dashboard')} - - - ), - onAction: () => onGenerateDashboard(), - }, - ]} - trigger={triggerProps => ( - - )} - /> - )} - - ) : ( - - {({ - hasReachedDashboardLimit, - isLoading: isLoadingDashboardsLimit, - limitMessage, - }) => ( - - )} - - ) - } - - - - - - + + {({hasFeature: hasAiGenerate}) => + hasAiGenerate && areAiFeaturesAllowed ? ( + + {({ + hasReachedDashboardLimit, + isLoading: isLoadingDashboardsLimit, + limitMessage, + }) => ( + onCreate(), + disabled: + hasReachedDashboardLimit || + isLoadingDashboardsLimit, + details: limitMessage, + }, + { + key: 'create-dashboard-agent', + textValue: t('Generate dashboard'), + label: ( + + {t('Generate dashboard')} + + + ), + onAction: () => onGenerateDashboard(), + }, + ]} + trigger={triggerProps => ( + + )} + /> + )} + + ) : ( + + {({ + hasReachedDashboardLimit, + isLoading: isLoadingDashboardsLimit, + limitMessage, + }) => ( + + )} + + ) + } + + + + + + + )} diff --git a/static/app/views/discover/landing.tsx b/static/app/views/discover/landing.tsx index b216603d865c34..0d5c4b11cc2f92 100644 --- a/static/app/views/discover/landing.tsx +++ b/static/app/views/discover/landing.tsx @@ -30,6 +30,8 @@ import {useNavigate} from 'sentry/utils/useNavigate'; import {useOrganization} from 'sentry/utils/useOrganization'; import {makeDiscoverPathname} from 'sentry/views/discover/pathnames'; import {getSavedQueryWithDataset} from 'sentry/views/discover/savedQuery/utils'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import QueryList from './queryList'; import {getPrebuiltQueries} from './utils'; @@ -132,6 +134,7 @@ const RENDER_PREBUILT_KEY = 'discover-render-prebuilt'; function DiscoverLanding() { const navigate = useNavigate(); const organization = useOrganization(); + const hasPageFrameFeature = useHasPageFrameFeature(); const location = useLocation(); const activeSort = useActiveSort(); const savedSearchQuery = useSavedSearchQuery(); @@ -201,21 +204,39 @@ function DiscoverLanding() { ]} /> - - { - trackAnalytics('discover_v2.build_new_query', { - organization, - }); - }} - > - {t('Build a new query')} - - + {hasPageFrameFeature ? ( + + { + trackAnalytics('discover_v2.build_new_query', { + organization, + }); + }} + > + {t('Build a new query')} + + + ) : ( + + { + trackAnalytics('discover_v2.build_new_query', { + organization, + }); + }} + > + {t('Build a new query')} + + + )} diff --git a/static/app/views/discover/results.tsx b/static/app/views/discover/results.tsx index 7857fa88e69e55..645c4f715a2ae6 100644 --- a/static/app/views/discover/results.tsx +++ b/static/app/views/discover/results.tsx @@ -67,7 +67,7 @@ import { DEFAULT_EVENT_VIEW_MAP, } from 'sentry/views/discover/results/data'; import ResultsChart from 'sentry/views/discover/results/resultsChart'; -import ResultsHeader from 'sentry/views/discover/results/resultsHeader'; +import {ResultsHeaderWrapper as ResultsHeader} from 'sentry/views/discover/results/resultsHeader'; import {ResultsSearchQueryBuilder} from 'sentry/views/discover/results/resultsSearchQueryBuilder'; import {SampleDataAlert} from 'sentry/views/discover/results/sampleDataAlert'; import Tags from 'sentry/views/discover/results/tags'; diff --git a/static/app/views/discover/results/resultsHeader.tsx b/static/app/views/discover/results/resultsHeader.tsx index be4157d5b4a964..0dbd7aaf63dfd0 100644 --- a/static/app/views/discover/results/resultsHeader.tsx +++ b/static/app/views/discover/results/resultsHeader.tsx @@ -1,4 +1,4 @@ -import {Component, Fragment} from 'react'; +import {Component, Fragment, type ComponentProps} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; @@ -19,11 +19,14 @@ import {EventInputName} from 'sentry/views/discover/eventInputName'; import SavedQueryButtonGroup from 'sentry/views/discover/savedQuery'; import {DatasetSelectorTabs} from 'sentry/views/discover/savedQuery/datasetSelectorTabs'; import {getSavedQueryWithDataset} from 'sentry/views/discover/savedQuery/utils'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; type Props = { api: Client; errorCode: number; eventView: EventView; + hasPageFrameFeature: boolean; location: Location; organization: Organization; setSavedQuery: (savedQuery?: SavedQuery) => void; @@ -123,6 +126,7 @@ class ResultsHeader extends Component { setSavedQuery, isHomepage, splitDecision, + hasPageFrameFeature, } = this.props; const {savedQuery, loading, homepageQuery} = this.state; const hasDiscoverQueryFeature = organization.features.includes('discover-query'); @@ -173,31 +177,59 @@ class ResultsHeader extends Component { )} {this.renderAuthor()} - - = 400 && errorCode < 500} - updateCallback={() => this.fetchData()} - yAxis={yAxis} - isHomepage={isHomepage} - setHomepageQuery={updatedHomepageQuery => { - this.setState({ - homepageQuery: getSavedQueryWithDataset( - updatedHomepageQuery - ) as SavedQuery, - }); - if (isHomepage) { - setSavedQuery(updatedHomepageQuery); - } - }} - homepageQuery={homepageQuery} - /> - + {hasPageFrameFeature ? ( + + = 400 && errorCode < 500} + updateCallback={() => this.fetchData()} + yAxis={yAxis} + isHomepage={isHomepage} + setHomepageQuery={updatedHomepageQuery => { + this.setState({ + homepageQuery: getSavedQueryWithDataset( + updatedHomepageQuery + ) as SavedQuery, + }); + if (isHomepage) { + setSavedQuery(updatedHomepageQuery); + } + }} + homepageQuery={homepageQuery} + /> + + ) : ( + + = 400 && errorCode < 500} + updateCallback={() => this.fetchData()} + yAxis={yAxis} + isHomepage={isHomepage} + setHomepageQuery={updatedHomepageQuery => { + this.setState({ + homepageQuery: getSavedQueryWithDataset( + updatedHomepageQuery + ) as SavedQuery, + }); + if (isHomepage) { + setSavedQuery(updatedHomepageQuery); + } + }} + homepageQuery={homepageQuery} + /> + + )} p.theme.space.xs} 0 0 0; `; -export default withApi(ResultsHeader); +const ResultsHeaderWithApi = withApi(ResultsHeader); + +type ResultsHeaderWrapperProps = Omit< + ComponentProps, + 'hasPageFrameFeature' +>; + +function ResultsHeaderWrapper(props: ResultsHeaderWrapperProps) { + const hasPageFrameFeature = useHasPageFrameFeature(); + return ; +} + +export {ResultsHeaderWrapper}; diff --git a/static/app/views/explore/logs/content.tsx b/static/app/views/explore/logs/content.tsx index 628aca2aa2cc9f..891f73698ad949 100644 --- a/static/app/views/explore/logs/content.tsx +++ b/static/app/views/explore/logs/content.tsx @@ -1,3 +1,5 @@ +import {Fragment} from 'react'; + import {LinkButton} from '@sentry/scraps/button'; import {Grid} from '@sentry/scraps/layout'; @@ -129,20 +131,25 @@ function LogsHeader() { {title ? title : t('Logs')} - - - {hasPageFrameFeature ? ( - - - {null} - + {hasPageFrameFeature ? ( + + {defined(onboardingProject) && ( + + - ) : ( - )} - {defined(onboardingProject) && } - - + + {null} + + + ) : ( + + + + {defined(onboardingProject) && } + + + )} ); } @@ -174,7 +181,7 @@ function SetupLogsButton() { priority="primary" href="https://docs.sentry.io/product/explore/logs/getting-started/" external - size="xs" + size="sm" onClick={() => { trackAnalytics('logs.explorer.setup_button_clicked', { organization, diff --git a/static/app/views/explore/multiQueryMode/index.tsx b/static/app/views/explore/multiQueryMode/index.tsx index bff601aa6b4a01..1c7d11adb27c3e 100644 --- a/static/app/views/explore/multiQueryMode/index.tsx +++ b/static/app/views/explore/multiQueryMode/index.tsx @@ -1,3 +1,5 @@ +import {Fragment} from 'react'; + import {Grid, Stack} from '@sentry/scraps/layout'; import Feature from 'sentry/components/acl/feature'; @@ -57,19 +59,29 @@ export default function MultiQueryMode() { /> {title ? title : t('Compare Queries')} - - - - {defined(id) && savedQuery?.isPrebuilt === false && } - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + + {defined(id) && savedQuery?.isPrebuilt === false && ( + + )} + + + {null} + + + ) : ( + + + + {defined(id) && savedQuery?.isPrebuilt === false && ( + + )} - )} - - + + + )} diff --git a/static/app/views/explore/savedQueries/index.tsx b/static/app/views/explore/savedQueries/index.tsx index 9881a1373569a6..e05c24319e5810 100644 --- a/static/app/views/explore/savedQueries/index.tsx +++ b/static/app/views/explore/savedQueries/index.tsx @@ -1,3 +1,4 @@ +import {Fragment} from 'react'; import {useNavigate} from 'react-router-dom'; import {Button, LinkButton} from '@sentry/scraps/button'; @@ -49,48 +50,83 @@ export default function SavedQueriesView() { {t('All Queries')} - - - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + {hasLogsFeature ? ( + ( + + )} + /> + ) : ( + } + size="sm" + to={getExploreUrl({organization, visualize: []})} + > + {t('Create Query')} + + )} + + + {null} + + + ) : ( + + - )} - {hasLogsFeature ? ( - ( - - )} - /> - ) : ( - } - size="sm" - to={getExploreUrl({organization, visualize: []})} - > - {t('Create Query')} - - )} - - + triggerProps.onClick?.(e); + }} + > + {t('Create Query')} + + )} + /> + ) : ( + } + size="sm" + to={getExploreUrl({organization, visualize: []})} + > + {t('Create Query')} + + )} + + + )} diff --git a/static/app/views/explore/spans/content.tsx b/static/app/views/explore/spans/content.tsx index 4a35fe2a32c44f..f3705c6e421152 100644 --- a/static/app/views/explore/spans/content.tsx +++ b/static/app/views/explore/spans/content.tsx @@ -1,5 +1,5 @@ +import {Fragment, useMemo} from 'react'; import type {ReactNode} from 'react'; -import {useMemo} from 'react'; import * as Sentry from '@sentry/react'; import {Grid, Stack} from '@sentry/scraps/layout'; @@ -181,19 +181,25 @@ function SpansTabHeader() { /> - - - - {defined(id) && savedQuery?.isPrebuilt === false && } - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + + {defined(id) && savedQuery?.isPrebuilt === false && } + + + {null} + + + ) : ( + + + + {defined(id) && savedQuery?.isPrebuilt === false && } - )} - - + + + )} ); } diff --git a/static/app/views/feedback/feedbackListPage.tsx b/static/app/views/feedback/feedbackListPage.tsx index 081fe13f8a8d86..9678794f88ddf5 100644 --- a/static/app/views/feedback/feedbackListPage.tsx +++ b/static/app/views/feedback/feedbackListPage.tsx @@ -41,8 +41,8 @@ const userFeedbackFeedbackOptions = { }; export default function FeedbackListPage() { - const organization = useOrganization(); const hasPageFrameFeature = useHasPageFrameFeature(); + const organization = useOrganization(); const {hasSetupOneFeedback} = useHaveSelectedProjectsSetupFeedback(); const pageFilters = usePageFilters(); @@ -161,40 +161,62 @@ export default function FeedbackListPage() { /> - - - {hasPageFrameFeature ? ( - - - {null} - - - ) : ( + {hasPageFrameFeature ? ( + + + } + to={{ + pathname: makeAlertsPathname({ + path: '/new/issue/', + organization, + }), + query: { + alert_option: 'issues', + referrer: 'feedback-list-page', + detectorType: 'metric_issue', + ...(feedbackProjectSlug ? {project: feedbackProjectSlug} : {}), + }, + }} + > + {t('Create Alert')} + + + + + {null} + + + + ) : ( + + - )} - } - to={{ - pathname: makeAlertsPathname({ - path: '/new/issue/', - organization, - }), - query: { - alert_option: 'issues', - referrer: 'feedback-list-page', - detectorType: 'metric_issue', - ...(feedbackProjectSlug ? {project: feedbackProjectSlug} : {}), - }, - }} - > - {t('Create Alert')} - - - + } + to={{ + pathname: makeAlertsPathname({ + path: '/new/issue/', + organization, + }), + query: { + alert_option: 'issues', + referrer: 'feedback-list-page', + detectorType: 'metric_issue', + ...(feedbackProjectSlug ? {project: feedbackProjectSlug} : {}), + }, + }} + > + {t('Create Alert')} + + + + )} diff --git a/static/app/views/insights/crons/components/monitorHeader.tsx b/static/app/views/insights/crons/components/monitorHeader.tsx index 5af89e2a441fda..6ae82295f8bd72 100644 --- a/static/app/views/insights/crons/components/monitorHeader.tsx +++ b/static/app/views/insights/crons/components/monitorHeader.tsx @@ -5,6 +5,8 @@ import {t} from 'sentry/locale'; import {useOrganization} from 'sentry/utils/useOrganization'; import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {Monitor} from 'sentry/views/insights/crons/types'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {MonitorHeaderActions} from './monitorHeaderActions'; @@ -16,6 +18,7 @@ interface Props { export function MonitorHeader({monitor, orgSlug, onUpdate}: Props) { const organization = useOrganization(); + const hasPageFrameFeature = useHasPageFrameFeature(); const crumbs = [ { label: t('Alerts'), @@ -41,9 +44,15 @@ export function MonitorHeader({monitor, orgSlug, onUpdate}: Props) { {monitor.name} - - - + {hasPageFrameFeature ? ( + + + + ) : ( + + + + )} ); } diff --git a/static/app/views/insights/crons/views/overview.tsx b/static/app/views/insights/crons/views/overview.tsx index ac5ef38aac13ba..5505f7eb6b311e 100644 --- a/static/app/views/insights/crons/views/overview.tsx +++ b/static/app/views/insights/crons/views/overview.tsx @@ -100,35 +100,57 @@ function CronsOverview() { /> - - - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + + {!guideVisible && ( + }> + {t('Add Cron Monitor')} + + )} + + + {null} + + + ) : ( + + - )} - - {!guideVisible && ( - }> - {t('Add Cron Monitor')} - - )} - - + + {!guideVisible && ( + }> + {t('Add Cron Monitor')} + + )} + + + )} diff --git a/static/app/views/insights/pages/domainViewHeader.tsx b/static/app/views/insights/pages/domainViewHeader.tsx index ebd6cc660f5065..9939f3dc700f0d 100644 --- a/static/app/views/insights/pages/domainViewHeader.tsx +++ b/static/app/views/insights/pages/domainViewHeader.tsx @@ -62,11 +62,11 @@ export function DomainViewHeader({ }: Props) { const organization = useOrganization(); const location = useLocation(); + const hasPageFrameFeature = useHasPageFrameFeature(); const moduleURLBuilder = useModuleURLBuilder(); const isLaravelInsightsAvailable = useIsLaravelInsightsAvailable(); const isNextJsInsightsAvailable = useIsNextJsInsightsAvailable(); const {view, isInOverviewPage} = useDomainViewFilters(); - const hasPageFrameFeature = useHasPageFrameFeature(); const isLaravelInsights = isLaravelInsightsAvailable && isInOverviewPage; const isNextJsInsights = isNextJsInsightsAvailable && isInOverviewPage; @@ -136,18 +136,23 @@ export function DomainViewHeader({ {crumbs.length > 1 && } {headerTitle || domainTitle} - - - {hasPageFrameFeature ? ( - - {null} - - ) : ( - + {hasPageFrameFeature ? ( + + {additonalHeaderActions && ( + {additonalHeaderActions} )} - {additonalHeaderActions} - - + + {null} + + + ) : ( + + + + {additonalHeaderActions} + + + )} {!hideDefaultTabs && ( diff --git a/static/app/views/insights/uptime/views/overview.tsx b/static/app/views/insights/uptime/views/overview.tsx index cc1c4154240d53..267dea159b2c26 100644 --- a/static/app/views/insights/uptime/views/overview.tsx +++ b/static/app/views/insights/uptime/views/overview.tsx @@ -85,27 +85,41 @@ export default function UptimeOverview() { /> - - - {hasPageFrameFeature ? ( - - {null} - - ) : ( + {hasPageFrameFeature ? ( + + + } + disabled={!canCreateAlert} + tooltipProps={{title: canCreateAlert ? undefined : permissionTooltipText}} + > + {t('Add Uptime Monitor')} + + + + {null} + + + ) : ( + + - )} - } - disabled={!canCreateAlert} - tooltipProps={{title: canCreateAlert ? undefined : permissionTooltipText}} - > - {t('Add Uptime Monitor')} - - - + } + disabled={!canCreateAlert} + tooltipProps={{title: canCreateAlert ? undefined : permissionTooltipText}} + > + {t('Add Uptime Monitor')} + + + + )} diff --git a/static/app/views/issueList/issueViews/issueViewsList/issueViewsList.tsx b/static/app/views/issueList/issueViews/issueViewsList/issueViewsList.tsx index 818e0721493c8a..02cf283e1275ed 100644 --- a/static/app/views/issueList/issueViews/issueViewsList/issueViewsList.tsx +++ b/static/app/views/issueList/issueViews/issueViewsList/issueViewsList.tsx @@ -333,10 +333,10 @@ const issueViewsFeedbackOptions = { }; export default function IssueViewsList() { + const hasPageFrameFeature = useHasPageFrameFeature(); const organization = useOrganization(); const navigate = useNavigate(); const location = useLocation(); - const hasPageFrameFeature = useHasPageFrameFeature(); const query = typeof location.query.query === 'string' ? location.query.query : ''; const {mutate: createGroupSearchView, isPending: isCreatingView} = useCreateGroupSearchView(); @@ -375,56 +375,97 @@ export default function IssueViewsList() { {t('All Views')} - - - {hasPageFrameFeature ? ( - - - {null} - - - ) : ( + {hasPageFrameFeature ? ( + + + ( + + } + > + {typeof props.children === 'function' + ? props.children(props) + : props.children} + + )} + > + {({hasFeature}) => ( + + )} + + + + + {null} + + + + ) : ( + + - )} - ( - - } - > - {typeof props.children === 'function' - ? props.children(props) - : props.children} - - )} - > - {({hasFeature}) => ( - - )} - - - + ( + + } + > + {typeof props.children === 'function' + ? props.children(props) + : props.children} + + )} + > + {({hasFeature}) => ( + + )} + + + + )} diff --git a/static/app/views/issueList/issueViewsHeader.tsx b/static/app/views/issueList/issueViewsHeader.tsx index 37b6b0ebeaf190..e9d88d8129177d 100644 --- a/static/app/views/issueList/issueViewsHeader.tsx +++ b/static/app/views/issueList/issueViewsHeader.tsx @@ -28,6 +28,8 @@ import {useUpdateGroupSearchViewStarred} from 'sentry/views/issueList/mutations/ import {makeFetchGroupSearchViewKey} from 'sentry/views/issueList/queries/useFetchGroupSearchView'; import type {GroupSearchView} from 'sentry/views/issueList/types'; import {useHasIssueViews} from 'sentry/views/navigation/secondary/sections/issues/issueViews/useHasIssueViews'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; type IssueViewsHeaderProps = { onRealtimeChange: (active: boolean) => void; @@ -218,11 +220,38 @@ export function IssueViewsHeader({ headerActions, }: IssueViewsHeaderProps) { const {viewId} = useParams<{viewId?: string}>(); + const hasPageFrameFeature = useHasPageFrameFeature(); const realtimeLabel = realtimeActive ? t('Pause real-time updates') : t('Enable real-time updates'); + if (hasPageFrameFeature) { + return ( + + + + + + {headerActions} + {!viewId && ( + + - - {project && ( - } - aria-label={t('Settings')} - to={`/settings/${organization.slug}/projects/${project.slug}/mobile-builds/`} - /> - )} - - - {({open: openDeleteModal}) => { - const menuItems: MenuItemProps[] = [ - { - key: 'rerun-status-checks', - label: ( - - - {t('Rerun Status Checks')} - - ), - onAction: handleRerunStatusChecksAction, - textValue: t('Rerun Status Checks'), - disabled: !canRerunStatusChecks, - tooltip: canRerunStatusChecks - ? undefined - : t('Size analysis must be completed to rerun status checks'), - }, - { - key: 'delete', - label: ( - - - {t('Delete Build')} - - ), - onAction: openDeleteModal, - textValue: t('Delete Build'), - }, - ]; + {hasPageFrameFeature ? ( + + + + + {project && ( + } + aria-label={t('Settings')} + to={`/settings/${organization.slug}/projects/${project.slug}/mobile-builds/`} + /> + )} + + + {({open: openDeleteModal}) => { + const menuItems: MenuItemProps[] = [ + { + key: 'rerun-status-checks', + label: ( + + + {t('Rerun Status Checks')} + + ), + onAction: handleRerunStatusChecksAction, + textValue: t('Rerun Status Checks'), + disabled: !canRerunStatusChecks, + tooltip: canRerunStatusChecks + ? undefined + : t('Size analysis must be completed to rerun status checks'), + }, + { + key: 'delete', + label: ( + + + {t('Delete Build')} + + ), + onAction: openDeleteModal, + textValue: t('Delete Build'), + }, + ]; - if (isSentryEmployee) { - menuItems.push({ - key: 'admin-section', - label: t('Admin (Sentry Employees only)'), - children: [ - { - key: 'rerun', - label: ( - - - {t('Rerun Analysis')} - - ), - onAction: handleRerunAction, - textValue: t('Rerun Analysis'), - }, - { - key: 'download', - label: ( - - - {t('Download Build')} - - ), - onAction: handleDownloadAction, - textValue: t('Download Build'), - }, - ], - }); - } + if (isSentryEmployee) { + menuItems.push({ + key: 'admin-section', + label: t('Admin (Sentry Employees only)'), + children: [ + { + key: 'rerun', + label: ( + + + {t('Rerun Analysis')} + + ), + onAction: handleRerunAction, + textValue: t('Rerun Analysis'), + }, + { + key: 'download', + label: ( + + + {t('Download Build')} + + ), + onAction: handleDownloadAction, + textValue: t('Download Build'), + }, + ], + }); + } - return ( - ( - - - - )} + return ( + ( + + + + )} + /> + ); + }} + + + + + {null} + + + + ) : ( + + + + + + {project && ( + } + aria-label={t('Settings')} + to={`/settings/${organization.slug}/projects/${project.slug}/mobile-builds/`} /> - ); - }} - - - + )} + + + {({open: openDeleteModal}) => { + const menuItems: MenuItemProps[] = [ + { + key: 'rerun-status-checks', + label: ( + + + {t('Rerun Status Checks')} + + ), + onAction: handleRerunStatusChecksAction, + textValue: t('Rerun Status Checks'), + disabled: !canRerunStatusChecks, + tooltip: canRerunStatusChecks + ? undefined + : t('Size analysis must be completed to rerun status checks'), + }, + { + key: 'delete', + label: ( + + + {t('Delete Build')} + + ), + onAction: openDeleteModal, + textValue: t('Delete Build'), + }, + ]; + + if (isSentryEmployee) { + menuItems.push({ + key: 'admin-section', + label: t('Admin (Sentry Employees only)'), + children: [ + { + key: 'rerun', + label: ( + + + {t('Rerun Analysis')} + + ), + onAction: handleRerunAction, + textValue: t('Rerun Analysis'), + }, + { + key: 'download', + label: ( + + + {t('Download Build')} + + ), + onAction: handleDownloadAction, + textValue: t('Download Build'), + }, + ], + }); + } + + return ( + ( + + + + )} + /> + ); + }} + + + + )} ); } diff --git a/static/app/views/preprod/snapshots/snapshots.tsx b/static/app/views/preprod/snapshots/snapshots.tsx index 0328df777a603a..d878d84d113272 100644 --- a/static/app/views/preprod/snapshots/snapshots.tsx +++ b/static/app/views/preprod/snapshots/snapshots.tsx @@ -16,6 +16,8 @@ import {useNavigate} from 'sentry/utils/useNavigate'; import {useOrganization} from 'sentry/utils/useOrganization'; import {useParams} from 'sentry/utils/useParams'; import {useResizableDrawer} from 'sentry/utils/useResizableDrawer'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {BuildError} from 'sentry/views/preprod/components/buildError'; import {BuildProcessing} from 'sentry/views/preprod/components/buildProcessing'; import {ComparisonState, getImageGroup} from 'sentry/views/preprod/types/snapshotTypes'; @@ -40,6 +42,7 @@ const DIFF_TYPE_ORDER: Record = Object.fromEntries( export default function SnapshotsPage() { const organization = useOrganization(); const theme = useTheme(); + const hasPageFrameFeature = useHasPageFrameFeature(); const {snapshotId} = useParams<{ snapshotId: string; }>(); @@ -403,13 +406,23 @@ export default function SnapshotsPage() { isSoloView={isSoloView} onToggleView={handleToggleView} /> - - - + {hasPageFrameFeature ? ( + + + + ) : ( + + + + )} {isComparisonProcessing ? processingContent : snapshotContent} diff --git a/static/app/views/projectsDashboard/index.tsx b/static/app/views/projectsDashboard/index.tsx index 0365f5e0e6dbb9..18b62642b79676 100644 --- a/static/app/views/projectsDashboard/index.tsx +++ b/static/app/views/projectsDashboard/index.tsx @@ -42,6 +42,8 @@ import {useTeamsById} from 'sentry/utils/useTeamsById'; import {useUser} from 'sentry/utils/useUser'; import {useUserTeams} from 'sentry/utils/useUserTeams'; import {TeamFilter} from 'sentry/views/alerts/list/rules/teamFilter'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {makeProjectsPathname} from 'sentry/views/projects/pathname'; import {ProjectCard} from './projectCard'; @@ -138,6 +140,7 @@ function Dashboard() { const navigate = useNavigate(); const location = useLocation(); const organization = useOrganization(); + const hasPageFrameFeature = useHasPageFrameFeature(); useEffect(() => { return function cleanup() { @@ -239,8 +242,8 @@ function Dashboard() { /> - - + {hasPageFrameFeature ? ( + } @@ -273,8 +276,45 @@ function Dashboard() { > {t('Create Project')} - - + + ) : ( + + + } + tooltipProps={{ + title: canJoinTeam + ? undefined + : t('You do not have permission to join a team.'), + }} + disabled={!canJoinTeam} + to={`/settings/${organization.slug}/teams/`} + data-test-id="join-team" + > + {t('Join a Team')} + + } + data-test-id="create-project" + > + {t('Create Project')} + + + + )} diff --git a/static/app/views/releases/detail/header/releaseHeader.tsx b/static/app/views/releases/detail/header/releaseHeader.tsx index 4e7261d7cf60fc..c1ab3a6b647be9 100644 --- a/static/app/views/releases/detail/header/releaseHeader.tsx +++ b/static/app/views/releases/detail/header/releaseHeader.tsx @@ -20,6 +20,8 @@ import type {Organization} from 'sentry/types/organization'; import type {Release, ReleaseMeta, ReleaseProject} from 'sentry/types/release'; import {formatAbbreviatedNumber} from 'sentry/utils/formatters'; import {normalizeUrl} from 'sentry/utils/url/normalizeUrl'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {isMobileRelease} from 'sentry/views/releases/utils'; import {makeReleasesPathname} from 'sentry/views/releases/utils/pathnames'; @@ -42,6 +44,7 @@ export function ReleaseHeader({ releaseMeta, refetchData, }: Props) { + const hasPageFrameFeature = useHasPageFrameFeature(); const {version, url} = release; const {commitCount, commitFilesChanged} = releaseMeta; @@ -165,14 +168,25 @@ export function ReleaseHeader({ - - - + {hasPageFrameFeature ? ( + + + + ) : ( + + + + )} diff --git a/static/app/views/replays/detail/header/replayDetailsHeaderActions.tsx b/static/app/views/replays/detail/header/replayDetailsHeaderActions.tsx index ec36ab8d06418a..9b0478c567cb1a 100644 --- a/static/app/views/replays/detail/header/replayDetailsHeaderActions.tsx +++ b/static/app/views/replays/detail/header/replayDetailsHeaderActions.tsx @@ -1,3 +1,4 @@ +import {Fragment} from 'react'; import styled from '@emotion/styled'; import {FeedbackButton} from 'sentry/components/feedbackButton/feedbackButton'; @@ -24,44 +25,67 @@ export function ReplayDetailsHeaderActions({readerResult}: Props) { renderThrottled={() => null} renderLoading={() => } renderMissing={() => null} - renderProcessingError={({replayRecord, projectSlug}) => ( - - {hasPageFrameFeature ? ( + renderProcessingError={({replayRecord, projectSlug}) => + hasPageFrameFeature ? ( + + + + + {null} - ) : ( + + ) : ( + - )} - - - - )} + + + + ) + } > - {({replay}) => ( - - {hasPageFrameFeature ? ( + {({replay}) => + hasPageFrameFeature ? ( + + + + + {null} - ) : ( + + ) : ( + - )} - - - - )} + + + + ) + } ); } diff --git a/static/app/views/replays/list.tsx b/static/app/views/replays/list.tsx index e151a7e3b52ba5..9288adc427bc98 100644 --- a/static/app/views/replays/list.tsx +++ b/static/app/views/replays/list.tsx @@ -30,6 +30,8 @@ import { useQueryParamsTitle, } from 'sentry/views/explore/queryParams/context'; import {TraceItemDataset} from 'sentry/views/explore/types'; +import {TopBar} from 'sentry/views/navigation/topBar'; +import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature'; import {ReplaysFilters} from 'sentry/views/replays/list/filters'; import {ReplayIndexContainer} from 'sentry/views/replays/list/replayIndexContainer'; import {ReplayIndexTimestampPrefPicker} from 'sentry/views/replays/list/replayIndexTimestampPrefPicker'; @@ -48,6 +50,7 @@ function ReplaysHeader() { const title = useQueryParamsTitle(); const organization = useOrganization(); const {data: savedQuery} = useGetSavedQuery(pageId); + const hasPageFrameFeature = useHasPageFrameFeature(); const hasSavedQueryTitle = defined(pageId) && defined(savedQuery) && savedQuery.name.length > 0; @@ -81,9 +84,15 @@ function ReplaysHeader() { )} - - - + {hasPageFrameFeature ? ( + + + + ) : ( + + + + )} ); }