import {Fragment, useState} from 'react'; import styled from '@emotion/styled'; import starImage from 'sentry-images/spot/banner-star.svg'; import ProjectAvatar from 'sentry/components/avatar/projectAvatar'; import FeatureBadge from 'sentry/components/badge/featureBadge'; import {Breadcrumbs as NavigationBreadcrumbs} from 'sentry/components/breadcrumbs'; import {Button} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import AutofixFeedback from 'sentry/components/events/autofix/autofixFeedback'; import {AutofixSetupContent} from 'sentry/components/events/autofix/autofixSetupModal'; import {AutofixSteps} from 'sentry/components/events/autofix/autofixSteps'; import {useAiAutofix} from 'sentry/components/events/autofix/useAutofix'; import {useAutofixSetup} from 'sentry/components/events/autofix/useAutofixSetup'; import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/components'; import {GroupSummaryBody, useGroupSummary} from 'sentry/components/group/groupSummary'; import Input from 'sentry/components/input'; import {IconDocs, IconSeer} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {EntryType, type Event} from 'sentry/types/event'; import type {Group} from 'sentry/types/group'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {getShortEventId} from 'sentry/utils/events'; import { getConfigForIssueType, shouldShowCustomErrorResourceConfig, } from 'sentry/utils/issueTypeConfig'; import {getRegionDataFromOrganization} from 'sentry/utils/regions'; import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams'; import useOrganization from 'sentry/utils/useOrganization'; import {MIN_NAV_HEIGHT} from 'sentry/views/issueDetails/streamline/eventTitle'; import Resources from 'sentry/views/issueDetails/streamline/resources'; import {useIsSampleEvent} from 'sentry/views/issueDetails/utils'; interface AutofixStartBoxProps { groupId: string; onSend: (message: string) => void; } function AutofixStartBox({onSend, groupId}: AutofixStartBoxProps) { const [message, setMessage] = useState(''); const send = () => { onSend(message); }; return ( {[...Array(8)].map((_, i) => ( ))} Autofix

Work together with Autofix to find the root cause and fix the issue.

setMessage(e.target.value)} placeholder={'(Optional) Share helpful context here...'} />
); } const shouldDisplayAiAutofixForOrganization = (organization: Organization) => { return ( ((organization.features.includes('autofix') && organization.features.includes('issue-details-autofix-ui')) || organization.genAIConsent) && !organization.hideAiFeatures && getRegionDataFromOrganization(organization)?.name !== 'de' ); }; // Autofix requires the event to have stack trace frames in order to work correctly. function hasStacktraceWithFrames(event: Event) { for (const entry of event.entries) { if (entry.type === EntryType.EXCEPTION) { if (entry.data.values?.some(value => value.stacktrace?.frames?.length)) { return true; } } if (entry.type === EntryType.THREADS) { if (entry.data.values?.some(thread => thread.stacktrace?.frames?.length)) { return true; } } } return false; } interface SolutionsHubDrawerProps { event: Event; group: Group; project: Project; } export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerProps) { const {autofixData, triggerAutofix, reset} = useAiAutofix(group, event); const {data: summaryData, isError} = useGroupSummary(group.id, group.issueCategory); const { data: setupData, isPending: isSetupLoading, refetch: refetchSetup, } = useAutofixSetup({ groupId: group.id, }); useRouteAnalyticsParams({ autofix_status: autofixData?.status ?? 'none', }); const config = getConfigForIssueType(group, project); const isSetupComplete = setupData?.integration.ok && setupData?.genAIConsent.ok; const hasSummary = summaryData && !isError && setupData?.genAIConsent.ok; const organization = useOrganization(); const isSampleError = useIsSampleEvent(); const displayAiAutofix = shouldDisplayAiAutofixForOrganization(organization) && config.autofix && !shouldShowCustomErrorResourceConfig(group, project) && hasStacktraceWithFrames(event) && !isSampleError; return ( {group.shortId} ), }, {label: getShortEventId(event.id)}, {label: t('Solutions Hub')}, ]} />
{t('Solutions Hub')}
{autofixData && ( )}
{config.resources && ( {t('Resources')} )} {t('Sentry AI')} , } )} /> {hasSummary && ( )} {displayAiAutofix && ( {!isSetupLoading && !isSetupComplete ? ( ) : !autofixData ? ( ) : ( )} )}
); } const ResourcesContainer = styled('div')``; const ResourcesBody = styled('div')` padding: 0 ${space(2)} ${space(2)} ${space(2)}; border-bottom: 1px solid ${p => p.theme.border}; margin-bottom: ${space(2)}; `; const Row = styled('div')` display: flex; gap: ${space(1)}; `; const StartBox = styled('div')` padding: ${space(2)}; position: absolute; bottom: ${space(2)}; left: ${space(2)}; right: ${space(2)}; `; const ContentContainer = styled('div')` position: relative; z-index: 1; margin-top: 80px; `; const SolutionsDrawerContainer = styled('div')` height: 100%; display: grid; grid-template-rows: auto auto 1fr; `; const SolutionsDrawerHeader = styled(DrawerHeader)` position: unset; max-height: ${MIN_NAV_HEIGHT}px; box-shadow: none; border-bottom: 1px solid ${p => p.theme.border}; `; const SolutionsDrawerNavigator = styled('div')` display: grid; grid-template-columns: 1fr auto; align-items: center; column-gap: ${space(1)}; padding: ${space(0.75)} 24px; background: ${p => p.theme.background}; z-index: 1; min-height: ${MIN_NAV_HEIGHT}px; box-shadow: ${p => p.theme.translucentBorder} 0 1px; `; const SolutionsDrawerBody = styled(DrawerBody)` overflow: auto; overscroll-behavior: contain; /* Move the scrollbar to the left edge */ scroll-margin: 0 ${space(2)}; direction: rtl; * { direction: ltr; } `; const Header = styled('h3')` display: block; font-size: ${p => p.theme.fontSizeExtraLarge}; font-weight: ${p => p.theme.fontWeightBold}; margin: 0; `; const NavigationCrumbs = styled(NavigationBreadcrumbs)` margin: 0; padding: 0; `; const CrumbContainer = styled('div')` display: flex; gap: ${space(1)}; align-items: center; `; const ShortId = styled('div')` font-family: ${p => p.theme.text.family}; font-size: ${p => p.theme.fontSizeMedium}; line-height: 1; `; const ButtonWithStars = styled('div')` position: relative; display: flex; align-items: center; `; const StarLarge = styled('img')` position: absolute; z-index: 0; filter: sepia(1) saturate(3) hue-rotate(290deg); `; const StarLarge1 = styled(StarLarge)` left: 45px; bottom: -15px; transform: rotate(90deg); width: 16px; height: 16px; `; const StarLarge2 = styled(StarLarge)` left: -5px; top: -15px; transform: rotate(-30deg); width: 24px; height: 24px; `; const StarLarge3 = styled(StarLarge)` right: -25px; bottom: 0px; transform: rotate(20deg); width: 28px; height: 28px; `; const SetupContainer = styled('div')` padding: ${space(2)}; /* Override some modal-specific styles */ h3 { font-size: ${p => p.theme.fontSizeLarge}; margin-bottom: ${space(2)}; } `; const StyledCard = styled('div')` background: ${p => p.theme.backgroundElevated}; border-radius: ${p => p.theme.borderRadius}; border: 1px solid ${p => p.theme.border}; overflow: hidden; box-shadow: ${p => p.theme.dropShadowMedium}; padding-bottom: ${space(1)}; `; const HeaderText = styled('div')` font-weight: bold; font-size: ${p => p.theme.fontSizeLarge}; display: flex; align-items: center; gap: ${space(0.5)}; padding-bottom: ${space(2)}; `; const StyledFeatureBadge = styled(FeatureBadge)` margin-left: ${space(0.25)}; padding-bottom: 3px; `; const ResourcesHeader = styled(HeaderText)` gap: ${space(1)}; `; const StarTrail = styled('div')` height: 400px; width: 100%; display: flex; justify-content: center; position: absolute; bottom: 7rem; left: 0; right: 0; z-index: -1; pointer-events: none; `; const TrailStar = styled('img')<{index: number; offset: number; size: number}>` position: absolute; width: ${p => p.size}px; height: ${p => p.size}px; top: ${p => p.index * 50}px; transform: translateX(${p => p.offset}px) rotate(${p => p.index * 40}deg); opacity: ${p => Math.min(1, 0.2 + p.index * 0.1)}; filter: sepia(1) saturate(3) hue-rotate(290deg); `;