solutionsDrawer.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import styled from '@emotion/styled';
  2. import bannerImage from 'sentry-images/insights/module-upsells/insights-module-upsell.svg';
  3. import ProjectAvatar from 'sentry/components/avatar/projectAvatar';
  4. import {Breadcrumbs as NavigationBreadcrumbs} from 'sentry/components/breadcrumbs';
  5. import {DrawerHeader} from 'sentry/components/globalDrawer/components';
  6. import {useGroupSummary} from 'sentry/components/group/groupSummary';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import type {Event} from 'sentry/types/event';
  10. import type {Group} from 'sentry/types/group';
  11. import {IssueCategory} from 'sentry/types/group';
  12. import type {Project} from 'sentry/types/project';
  13. import {getShortEventId} from 'sentry/utils/events';
  14. import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
  15. import marked from 'sentry/utils/marked';
  16. import {MIN_NAV_HEIGHT} from 'sentry/views/issueDetails/streamline/eventTitle';
  17. import Resources from 'sentry/views/issueDetails/streamline/resources';
  18. interface SolutionsDrawerProps {
  19. group: Group;
  20. project: Project;
  21. event?: Event;
  22. }
  23. export function SolutionsDrawer({group, project, event}: SolutionsDrawerProps) {
  24. const {data, hasGenAIConsent} = useGroupSummary(group.id, group.issueCategory);
  25. const config = getConfigForIssueType(group, project);
  26. return (
  27. <SolutionsDrawerContainer>
  28. <SolutionsDrawerHeader>
  29. <NavigationCrumbs
  30. crumbs={[
  31. {
  32. label: (
  33. <CrumbContainer>
  34. <ProjectAvatar project={project} />
  35. <ShortId>{group.shortId}</ShortId>
  36. </CrumbContainer>
  37. ),
  38. },
  39. {label: event ? getShortEventId(event.id) : ''},
  40. {label: t('Solutions & Resources')},
  41. ]}
  42. />
  43. </SolutionsDrawerHeader>
  44. <Content>
  45. <Header>{t('Solution Center')}</Header>
  46. <IllustrationContainer>
  47. <Illustration src={bannerImage} />
  48. </IllustrationContainer>
  49. {hasGenAIConsent && group.issueCategory === IssueCategory.ERROR && data && (
  50. <GroupSummaryWrapper>
  51. <SummaryHeader>{t('Issue Summary')}</SummaryHeader>
  52. <HeadlineContent>{data.headline}</HeadlineContent>
  53. <SummaryContent
  54. dangerouslySetInnerHTML={{
  55. __html: marked(data.summary),
  56. }}
  57. />
  58. </GroupSummaryWrapper>
  59. )}
  60. {config.resources && (
  61. <Resources
  62. eventPlatform={event?.platform}
  63. group={group}
  64. configResources={config.resources}
  65. />
  66. )}
  67. </Content>
  68. </SolutionsDrawerContainer>
  69. );
  70. }
  71. const SummaryHeader = styled('div')`
  72. font-weight: ${p => p.theme.fontWeightBold};
  73. font-size: ${p => p.theme.fontSizeLarge};
  74. `;
  75. const HeadlineContent = styled('span')`
  76. overflow-wrap: break-word;
  77. p {
  78. margin: 0;
  79. }
  80. code {
  81. word-break: break-all;
  82. }
  83. width: 100%;
  84. `;
  85. const SummaryContent = styled('div')`
  86. overflow-wrap: break-word;
  87. p {
  88. margin: 0;
  89. }
  90. code {
  91. word-break: break-all;
  92. }
  93. `;
  94. const GroupSummaryWrapper = styled('div')`
  95. display: flex;
  96. flex-direction: column;
  97. gap: ${space(0.5)};
  98. `;
  99. const IllustrationContainer = styled('div')`
  100. display: flex;
  101. justify-content: center;
  102. `;
  103. const Content = styled('div')`
  104. display: flex;
  105. flex-direction: column;
  106. gap: ${space(2)};
  107. padding: 24px;
  108. `;
  109. const Illustration = styled('img')`
  110. height: 100%;
  111. `;
  112. const SolutionsDrawerContainer = styled('div')`
  113. height: 100%;
  114. display: grid;
  115. grid-template-rows: auto auto 1fr;
  116. `;
  117. const SolutionsDrawerHeader = styled(DrawerHeader)`
  118. position: unset;
  119. max-height: ${MIN_NAV_HEIGHT}px;
  120. box-shadow: none;
  121. border-bottom: 1px solid ${p => p.theme.border};
  122. `;
  123. const Header = styled('h3')`
  124. display: block;
  125. font-size: ${p => p.theme.headerFontSize};
  126. font-weight: ${p => p.theme.fontWeightBold};
  127. margin: 0;
  128. `;
  129. const NavigationCrumbs = styled(NavigationBreadcrumbs)`
  130. margin: 0;
  131. padding: 0;
  132. `;
  133. const CrumbContainer = styled('div')`
  134. display: flex;
  135. gap: ${space(1)};
  136. align-items: center;
  137. `;
  138. const ShortId = styled('div')`
  139. font-family: ${p => p.theme.text.family};
  140. font-size: ${p => p.theme.fontSizeMedium};
  141. line-height: 1;
  142. `;