import {useState} from 'react'; import {browserHistory} from 'react-router'; import styled from '@emotion/styled'; import {Location} from 'history'; import Feature from 'sentry/components/acl/feature'; import Alert from 'sentry/components/alert'; import * as Layout from 'sentry/components/layouts/thirds'; import NoProjectMessage from 'sentry/components/noProjectMessage'; import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {t} from 'sentry/locale'; import {PageContent} from 'sentry/styles/organization'; import {Organization, Project} from 'sentry/types'; import {defined} from 'sentry/utils'; import EventView from 'sentry/utils/discover/eventView'; import {PerformanceEventViewProvider} from 'sentry/utils/performance/contexts/performanceEventViewContext'; import {decodeScalar} from 'sentry/utils/queryString'; import {getTransactionName} from '../utils'; import TransactionHeader from './header'; import Tab from './tabs'; import {TransactionThresholdMetric} from './transactionThresholdModal'; export type ChildProps = { eventView: EventView; location: Location; organization: Organization; projectId: string; projects: Project[]; setError: React.Dispatch>; transactionName: string; // These are used to trigger a reload when the threshold/metric changes. transactionThreshold?: number; transactionThresholdMetric?: TransactionThresholdMetric; }; type Props = { childComponent: (props: ChildProps) => JSX.Element; generateEventView: (props: {location: Location; transactionName: string}) => EventView; getDocumentTitle: (name: string) => string; location: Location; organization: Organization; projects: Project[]; tab: Tab; features?: string[]; }; function PageLayout(props: Props) { const { location, organization, projects, tab, getDocumentTitle, generateEventView, childComponent: ChildComponent, features = [], } = props; const projectId = decodeScalar(location.query.project); const transactionName = getTransactionName(location); const [error, setError] = useState(); const [transactionThreshold, setTransactionThreshold] = useState(); const [transactionThresholdMetric, setTransactionThresholdMetric] = useState< TransactionThresholdMetric | undefined >(); const [incompatibleAlertNotice, setIncompatibleAlertNotice] = useState(null); if (!defined(projectId) || !defined(transactionName)) { redirectToPerformanceHomepage(organization, location); return null; } const project = projects.find(p => p.id === projectId); const handleIncompatibleQuery = (incompatibleAlertNoticeFn, _errors) => { const notice = incompatibleAlertNoticeFn(() => setIncompatibleAlertNotice(null)); setIncompatibleAlertNotice(notice); }; const eventView = generateEventView({location, transactionName}); return ( { setTransactionThreshold(threshold); setTransactionThresholdMetric(metric); }} /> {defined(error) && ( {error} )} {incompatibleAlertNotice && ( {incompatibleAlertNotice} )} ); } export function NoAccess() { return {t("You don't have access to this feature")}; } const StyledPageContent = styled(PageContent)` padding: 0; `; const StyledAlert = styled(Alert)` grid-column: 1/3; margin: 0; `; export function redirectToPerformanceHomepage( organization: Organization, location: Location ) { // If there is no transaction name, redirect to the Performance landing page browserHistory.replace({ pathname: `/organizations/${organization.slug}/performance/`, query: { ...location.query, }, }); } export default PageLayout;