import {useEffect, useMemo} from 'react'; import {browserHistory} from 'react-router'; import styled from '@emotion/styled'; import qs from 'qs'; import connectDotsImg from 'sentry-images/spot/performance-connect-dots.svg'; import {Alert} from 'sentry/components/alert'; import {Button} from 'sentry/components/button'; import {DropdownMenu} from 'sentry/components/dropdownMenu'; import ExternalLink from 'sentry/components/links/externalLink'; import {SidebarPanelKey} from 'sentry/components/sidebar/types'; import {withPerformanceOnboarding} from 'sentry/data/platformCategories'; import {IconClose} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import SidebarPanelStore from 'sentry/stores/sidebarPanelStore'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import useDismissAlert from 'sentry/utils/useDismissAlert'; import useProjects from 'sentry/utils/useProjects'; import {traceAnalytics} from '../traceAnalytics'; import type {TraceTree} from '../traceModels/traceTree'; import {TraceType} from '../traceType'; type OnlyOrphanErrorWarningsProps = { organization: Organization; traceSlug: string | undefined; tree: TraceTree; }; function filterProjects(projects: Project[], tree: TraceTree) { const projectsWithNoPerformance: Project[] = []; const projectsWithOnboardingChecklist: Project[] = []; for (const project of projects) { if (tree.project_ids.has(Number(project.id))) { if (!project.firstTransactionEvent) { projectsWithNoPerformance.push(project); if (project.platform && withPerformanceOnboarding.has(project.platform)) { projectsWithOnboardingChecklist.push(project); } } } } return {projectsWithNoPerformance, projectsWithOnboardingChecklist}; } export function PerformanceSetupWarning({ traceSlug, tree, organization, }: OnlyOrphanErrorWarningsProps) { const {projects} = useProjects(); const {projectsWithNoPerformance, projectsWithOnboardingChecklist} = useMemo(() => { return filterProjects(projects, tree); }, [projects, tree]); const LOCAL_STORAGE_KEY = `${traceSlug}:performance-orphan-error-onboarding-banner-hide`; useEffect(() => { if ( projectsWithOnboardingChecklist.length > 0 && location.hash === '#performance-sidequest' ) { SidebarPanelStore.activatePanel(SidebarPanelKey.PERFORMANCE_ONBOARDING); } }, [projectsWithOnboardingChecklist]); const {dismiss: snooze, isDismissed: isSnoozed} = useDismissAlert({ key: LOCAL_STORAGE_KEY, expirationDays: 7, }); const {dismiss, isDismissed} = useDismissAlert({ key: LOCAL_STORAGE_KEY, expirationDays: 365, }); if ( tree.type !== 'trace' || tree.shape !== TraceType.ONLY_ERRORS || projectsWithNoPerformance.length === 0 ) { return null; } if (projectsWithOnboardingChecklist.length === 0) { return ( {tct( "Some of the projects associated with this trace don't support performance monitoring. To learn more about how to setup performance monitoring, visit our [documentation].", { documentationLink: ( {t('documentation')} ), } )} ); } if (isDismissed || isSnoozed) { return null; } return ( {t('Your setup is incomplete')} {t( "Want to know why this string of errors happened? Configure performance monitoring to get a full picture of what's going on." )} {} , }} size="xs" items={[ { key: 'dismiss', label: t('Dismiss'), onAction: () => { dismiss(); }, }, { key: 'snooze', label: t('Snooze'), onAction: () => { snooze(); }, }, ]} /> ); } const BannerWrapper = styled('div')` position: relative; border: 1px solid ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; padding: ${space(2)} ${space(3)}; margin-bottom: ${space(2)}; background: linear-gradient( 90deg, ${p => p.theme.backgroundSecondary}00 0%, ${p => p.theme.backgroundSecondary}FF 70%, ${p => p.theme.backgroundSecondary}FF 100% ); container-type: inline-size; `; const ActionsWrapper = styled('div')` max-width: 50%; `; const ButtonsWrapper = styled('div')` display: flex; align-items: center; gap: ${space(0.5)}; `; const BannerTitle = styled('div')` font-size: ${p => p.theme.fontSizeExtraLarge}; margin-bottom: ${space(1)}; font-weight: ${p => p.theme.fontWeightBold}; `; const BannerDescription = styled('div')` margin-bottom: ${space(1.5)}; `; const CloseDropdownMenu = styled(DropdownMenu)` position: absolute; display: block; top: ${space(1)}; right: ${space(1)}; color: ${p => p.theme.white}; cursor: pointer; z-index: 1; `; const Background = styled('div')<{image: any}>` display: flex; justify-self: flex-end; position: absolute; top: 14px; right: 15px; height: 81%; width: 100%; max-width: 413px; background-image: url(${p => p.image}); background-repeat: no-repeat; background-size: contain; @container (max-width: 840px) { display: none; } `; const ActionButton = styled('div')` display: flex; gap: ${space(1)}; `;