eventsTableRow.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import {Component} from 'react';
  2. import UserAvatar from 'sentry/components/avatar/userAvatar';
  3. import DateTime from 'sentry/components/dateTime';
  4. import {DeviceName} from 'sentry/components/deviceName';
  5. import FileSize from 'sentry/components/fileSize';
  6. import GlobalSelectionLink from 'sentry/components/globalSelectionLink';
  7. import {AvatarUser, Organization, Tag} from 'sentry/types';
  8. import {Event} from 'sentry/types/event';
  9. import AttachmentUrl from 'sentry/utils/attachmentUrl';
  10. import withOrganization from 'sentry/utils/withOrganization';
  11. type Props = {
  12. event: Event;
  13. groupId: string;
  14. orgId: string;
  15. organization: Organization;
  16. projectId: string;
  17. tagList: Tag[];
  18. hasUser?: boolean;
  19. } & React.HTMLAttributes<HTMLDivElement>;
  20. class EventsTableRow extends Component<Props> {
  21. renderCrashFileLink() {
  22. const {event, projectId} = this.props;
  23. if (!event.crashFile) {
  24. return null;
  25. }
  26. const crashFileType =
  27. event.crashFile.type === 'event.minidump' ? 'Minidump' : 'Crash file';
  28. return (
  29. <AttachmentUrl
  30. projectId={projectId}
  31. eventId={event.id}
  32. attachment={event.crashFile}
  33. >
  34. {url =>
  35. url ? (
  36. <small>
  37. {crashFileType}: <a href={`${url}?download=1`}>{event.crashFile?.name}</a> (
  38. <FileSize bytes={event.crashFile?.size || 0} />)
  39. </small>
  40. ) : null
  41. }
  42. </AttachmentUrl>
  43. );
  44. }
  45. render() {
  46. const {className, event, orgId, groupId, tagList, hasUser} = this.props;
  47. const tagMap = {};
  48. event.tags.forEach(tag => {
  49. tagMap[tag.key] = tag.value;
  50. });
  51. const link = `/organizations/${orgId}/issues/${groupId}/events/${event.id}/`;
  52. return (
  53. <tr key={event.id} className={className}>
  54. <td>
  55. <h5>
  56. <GlobalSelectionLink to={link}>
  57. <DateTime date={event.dateCreated} year seconds timeZone />
  58. </GlobalSelectionLink>
  59. <small>{event.title.substr(0, 100)}</small>
  60. {this.renderCrashFileLink()}
  61. </h5>
  62. </td>
  63. {hasUser && (
  64. <td className="event-user table-user-info">
  65. {event.user ? (
  66. <div>
  67. <UserAvatar
  68. user={event.user as AvatarUser} // TODO(ts): Some of the user fields are optional from event, this cast can probably be removed in the future
  69. size={24}
  70. className="avatar"
  71. gravatar={false}
  72. />
  73. {event.user.email}
  74. </div>
  75. ) : (
  76. <span>—</span>
  77. )}
  78. </td>
  79. )}
  80. {tagList.map(tag => (
  81. <td key={tag.key}>
  82. <div>
  83. {tag.key === 'device' ? (
  84. <DeviceName value={tagMap[tag.key]} />
  85. ) : (
  86. tagMap[tag.key]
  87. )}
  88. </div>
  89. </td>
  90. ))}
  91. </tr>
  92. );
  93. }
  94. }
  95. export {EventsTableRow};
  96. export default withOrganization(EventsTableRow);