eventEntries.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Location} from 'history';
  4. import {CommitRow} from 'sentry/components/commitRow';
  5. import {EventEvidence} from 'sentry/components/events/eventEvidence';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {
  9. Entry,
  10. Event,
  11. Group,
  12. Organization,
  13. Project,
  14. SharedViewOrganization,
  15. } from 'sentry/types';
  16. import {isNotSharedOrganization} from 'sentry/types/utils';
  17. import {objectIsEmpty} from 'sentry/utils';
  18. import {EventContexts} from './contexts';
  19. import {EventDevice} from './device';
  20. import {EventAttachments} from './eventAttachments';
  21. import {EventCause} from './eventCause';
  22. import {EventDataSection} from './eventDataSection';
  23. import {EventEntry} from './eventEntry';
  24. import {EventErrors} from './eventErrors';
  25. import {EventExtraData} from './eventExtraData';
  26. import {EventSdk} from './eventSdk';
  27. import {EventTagsAndScreenshot} from './eventTagsAndScreenshot';
  28. import {EventViewHierarchy} from './eventViewHierarchy';
  29. import {EventGroupingInfo} from './groupingInfo';
  30. import {EventPackageData} from './packageData';
  31. import {EventRRWebIntegration} from './rrwebIntegration';
  32. import {EventSdkUpdates} from './sdkUpdates';
  33. import {DataSection} from './styles';
  34. import {EventUserFeedback} from './userFeedback';
  35. type Props = {
  36. location: Location;
  37. /**
  38. * The organization can be the shared view on a public issue view.
  39. */
  40. organization: Organization | SharedViewOrganization;
  41. project: Project;
  42. className?: string;
  43. event?: Event;
  44. group?: Group;
  45. isShare?: boolean;
  46. showTagSummary?: boolean;
  47. };
  48. const EventEntries = ({
  49. organization,
  50. project,
  51. location,
  52. event,
  53. group,
  54. className,
  55. isShare = false,
  56. showTagSummary = true,
  57. }: Props) => {
  58. const orgSlug = organization.slug;
  59. const projectSlug = project.slug;
  60. const orgFeatures = organization?.features ?? [];
  61. if (!event) {
  62. return (
  63. <LatestEventNotAvailable>
  64. <h3>{t('Latest Event Not Available')}</h3>
  65. </LatestEventNotAvailable>
  66. );
  67. }
  68. const hasContext = !objectIsEmpty(event.user ?? {}) || !objectIsEmpty(event.contexts);
  69. return (
  70. <div className={className}>
  71. <EventErrors event={event} project={project} isShare={isShare} />
  72. {!isShare && isNotSharedOrganization(organization) && (
  73. <EventCause
  74. project={project}
  75. eventId={event.id}
  76. group={group}
  77. commitRow={CommitRow}
  78. />
  79. )}
  80. {event.userReport && group && (
  81. <EventDataSection title="User Feedback" type="user-feedback">
  82. <EventUserFeedback
  83. report={event.userReport}
  84. orgId={orgSlug}
  85. issueId={group.id}
  86. />
  87. </EventDataSection>
  88. )}
  89. {showTagSummary && (
  90. <EventTagsAndScreenshot
  91. event={event}
  92. organization={organization as Organization}
  93. projectSlug={projectSlug}
  94. location={location}
  95. isShare={isShare}
  96. />
  97. )}
  98. <EventEvidence event={event} projectSlug={project.slug} />
  99. <Entries
  100. definedEvent={event}
  101. projectSlug={projectSlug}
  102. group={group}
  103. organization={organization}
  104. isShare={isShare}
  105. />
  106. {hasContext && <EventContexts group={group} event={event} />}
  107. <EventExtraData event={event} />
  108. <EventPackageData event={event} />
  109. <EventDevice event={event} />
  110. {!isShare && <EventViewHierarchy event={event} project={project} />}
  111. {!isShare && <EventAttachments event={event} projectSlug={projectSlug} />}
  112. <EventSdk sdk={event.sdk} meta={event._meta?.sdk} />
  113. {!isShare && <EventSdkUpdates event={event} />}
  114. {!isShare && event.groupID && (
  115. <EventGroupingInfo
  116. projectSlug={projectSlug}
  117. event={event}
  118. showGroupingConfig={
  119. orgFeatures.includes('set-grouping-config') && 'groupingConfig' in event
  120. }
  121. />
  122. )}
  123. {!isShare && (
  124. <EventRRWebIntegration event={event} orgId={orgSlug} projectSlug={projectSlug} />
  125. )}
  126. </div>
  127. );
  128. };
  129. function Entries({
  130. definedEvent,
  131. projectSlug,
  132. isShare,
  133. group,
  134. organization,
  135. }: {
  136. definedEvent: Event;
  137. projectSlug: string;
  138. isShare?: boolean;
  139. } & Pick<Props, 'group' | 'organization'>) {
  140. if (!Array.isArray(definedEvent.entries)) {
  141. return null;
  142. }
  143. return (
  144. <Fragment>
  145. {(definedEvent.entries as Array<Entry>).map((entry, entryIdx) => (
  146. <EventEntry
  147. key={entryIdx}
  148. projectSlug={projectSlug}
  149. group={group}
  150. organization={organization}
  151. event={definedEvent}
  152. entry={entry}
  153. isShare={isShare}
  154. />
  155. ))}
  156. </Fragment>
  157. );
  158. }
  159. const LatestEventNotAvailable = styled('div')`
  160. padding: ${space(2)} ${space(4)};
  161. `;
  162. const BorderlessEventEntries = styled(EventEntries)`
  163. & ${DataSection} {
  164. margin-left: 0 !important;
  165. margin-right: 0 !important;
  166. padding: ${space(3)} 0 0 0;
  167. }
  168. & ${DataSection}:first-child {
  169. padding-top: 0;
  170. border-top: 0;
  171. }
  172. `;
  173. export {EventEntries, BorderlessEventEntries};